# -*- coding: utf-8 -*-
"""
/***************************************************************************
 SUEWSPrepareDatabase
                                 A QGIS plugin
 Prepares inputdata for the SUEWS model using typology database
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2022-03-22
        git sha              : $Format:%H$
        copyright            : (C) 2022 by Fredrik Lindberg, Oskar Bäcklin
        email                : fredrikl@gvc.gu.se
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""

from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import  QAction, QMessageBox, QFileDialog
from qgis.utils import iface
from osgeo import gdal, osr

from qgis.core import (QgsMapLayerProxyModel, 
                       QgsFieldProxyModel, 
                       QgsVectorLayer, 
                       QgsFeature, 
                       QgsProject,
                       QgsVectorFileWriter,
                       Qgis)

# Initialize Qt resources from file resources.py
from .resources import *
# Import the code for the dialog
from .suews_prepare_database_dialog import SUEWSPrepareDatabaseDialog
import os.path
import webbrowser

#These are not needed here later
import copy
import numpy as np
import pandas as pd
import os
import yaml
import sys

from ..Utilities import RoughnessCalcFunctionV2 as rg
from ..Utilities.misc import saveraster
from .Utilities.db_functions import * 
from .Utilities.ssParms import getVertheights, ss_calc_gridlayout
from shutil import rmtree

# used for pydantic testing
# from pathlib import Path
try:
    from supy.cmd.validate_config import validate_single_file
    from supy.data_model.schema.publisher import generate_json_schema
except:
    pass

# from .prepare_workertypo import Worker
import processing

################################################################
class SUEWSPrepareDatabase(object):
    """QGIS Plugin Implementation."""

    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(
            self.plugin_dir,
            'i18n',
            'SUEWSPrepareDatabase_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)
            QCoreApplication.installTranslator(self.translator)

        self.layerComboManagerPolygrid = None
        self.layerComboManagerPolyField = None
        self.fieldgen = None
        self.LCF_Paved = None
        self.LCF_Buildings = None
        self.LCF_Evergreen = None
        self.LCF_Decidious = None
        self.LCF_Grass = None
        self.LCF_Baresoil = None
        self.LCF_Water = None
        self.pop_density = None
        self.pop_density_day = None
        self.IMP_mean_height = None
        self.IMP_z0 = None
        self.IMP_zd = None
        self.IMP_fai = None
        self.IMPveg_mean_height_dec = None
        self.IMPveg_mean_height_eve = None
        self.IMPveg_fai_dec = None
        self.IMPveg_fai_eve = None
        self.checkBox_twovegfiles = None
        self.IMPvegfile_path_dec = None
        self.IMPvegfile_path_eve = None
        # self.wall_area = None
        self.daypop = 0
        self.typologies = 0
        self.start_DLS = 85
        self.end_DLS = 302
        self.day_since_rain = 0
        self.leaf_cycle = 1
        self.soil_moisture = 100
        self.utc = 1
        self.file_code = ''
        self.steps = 0
        # self.region = None
        self.defTypo = None
        self.twovegfiles = 0

        self.output_dir = None
        self.LCFfile_path = None
        self.IMPfile_path = None
        self.IMPvegfile_path = None
        self.Metfile_path = None
        self.LCF_from_file = True
        self.IMP_from_file = True
        self.IMPveg_from_file = True

        self.spartacus = 0
        self.vertheights = '10, 20'
        self.nlayers = 3
        self.skew = 2
        self.heightMethod = 0

        # self.comboBoxRegion = True
        self.comboBoxCountry = True
        self.comboBoxPaved = True
        self.comboBoxBareSoil = True
        self.comboBoxBuilding = True
        self.comboBoxGrass = True
        self.comboBoxEvrTree = True
        self.comboBoxDecTree = True
        # self.lclayer = None

        
    def tr(self, message):
        return QCoreApplication.translate('SUEWSPrepareDatabase', message)


    def initGui(self):
        # """Create the menu entries and toolbar icons inside the QGIS GUI."""
        
        # Create action that will start plugin configuration
        self.action = QAction(
            QIcon(':/plugins/suews_prepare_database/icon.png'),
            'SUEWS Prepare - Database Typologies', self.iface.mainWindow())
        # connect the action to the run method
        self.action.triggered.connect(self.run)

        # Add toolbar button and menu item
        self.iface.addToolBarIcon(self.action)
        self.iface.addPluginToMenu(self.tr("&SUEWS Prepare - Database Typologies"), self.action)


    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""

        self.iface.removePluginMenu("&SUEWS Prepare - Database Typologies", self.action)
        self.iface.removeToolBarIcon(self.action)


    def run(self):

        try:
            import supy
        except Exception as e:
            QMessageBox.critical(None, 'SuPy library missing', 'This plugin requires the supy package to be installed OR upgraded. '
                                                'See Section 2.3 in the UMEP-manual for further information on how ' 
                                                'to install missing/external python packages in QGIS3.')
            return

        self.supylib = sys.modules["supy"].__path__[0]
        
        # Declare instance attributes
        self.dlg = SUEWSPrepareDatabaseDialog()
        self.actions = []
        self.menu = self.tr(u'&SUEWS Prepare (Database Typologies)')
        self.dlg.helpButton.clicked.connect(self.help)

        self.dlg.outputButton.clicked.connect(self.set_output_folder)
        self.dlg.runButton.clicked.connect(self.generate)

        self.outputDialog = QFileDialog()
        self.outputDialog.setFileMode(QFileDialog.FileMode.Directory)
        self.outputDialog.setOption(QFileDialog.Option.ShowDirsOnly, True)

        self.SSDialog = QFileDialog()
        self.SSDialog.setFileMode(QFileDialog.FileMode.Directory)
        self.SSDialog.setOption(QFileDialog.Option.ShowDirsOnly, True)

        self.fileDialog = QFileDialog()
        self.fileDialog.setFileMode(QFileDialog.FileMode.ExistingFile)

        self.fileDialogISO = QFileDialog()
        self.fileDialogISO.setNameFilter("(*_isotropic.txt)")
        self.fileDialogISO.setFileMode(QFileDialog.FileMode.ExistingFile)

        # Read Database
        self.db_path = self.plugin_dir.split('suews_prepare_database')[0] + 'suews_database_manager/data/database.xlsx'  
        db_dict = read_DB(self.db_path)

        # Filling comboboxes in frame for Regional Parameters
        # 'nameOrigin' is a column created in read_DB to have a readable and understandable indexer shown to the user instead of using the ID
        # The lists here are the ones populating comboboxes for regional parameters
        building_list = db_dict['NonVeg']['nameOrigin'].loc[db_dict['NonVeg']['Surface'] == 'Buildings']
        paved_list =    db_dict['NonVeg']['nameOrigin'].loc[db_dict['NonVeg']['Surface'] == 'Paved']
        bsoil_list =    db_dict['NonVeg']['nameOrigin'].loc[db_dict['NonVeg']['Surface'] == 'Bare Soil']
        grass_list =    db_dict['Veg']['nameOrigin'].loc[db_dict['Veg']['Surface'] == 'Grass']
        dec_tree_list = db_dict['Veg']['nameOrigin'].loc[db_dict['Veg']['Surface'] == 'Deciduous Tree']
        evr_tree_list = db_dict['Veg']['nameOrigin'].loc[db_dict['Veg']['Surface'] == 'Evergreen Tree']
        anthro_list = db_dict['AnthropogenicEmission']['nameOrigin'] #steg1
        traffic_listWD = db_dict['Profiles']['nameOrigin'].loc[db_dict['Profiles']['Profile Type'] == 'Traffic'].loc[db_dict['Profiles']['Day'] == 'Weekday']
        traffic_listWE = db_dict['Profiles']['nameOrigin'].loc[db_dict['Profiles']['Profile Type'] == 'Traffic'].loc[db_dict['Profiles']['Day'] == 'Weekend']
        human_listWD = db_dict['Profiles']['nameOrigin'].loc[db_dict['Profiles']['Profile Type'] == 'Human activity'].loc[db_dict['Profiles']['Day'] == 'Weekday']
        human_listWE = db_dict['Profiles']['nameOrigin'].loc[db_dict['Profiles']['Profile Type'] == 'Human activity'].loc[db_dict['Profiles']['Day'] == 'Weekend']
        popdens_listWD = db_dict['Profiles']['nameOrigin'].loc[db_dict['Profiles']['Profile Type'] == 'Population density'].loc[db_dict['Profiles']['Day'] == 'Weekday']
        popdens_listWE = db_dict['Profiles']['nameOrigin'].loc[db_dict['Profiles']['Profile Type'] == 'Population density'].loc[db_dict['Profiles']['Day'] == 'Weekend']
        energy_listWD = db_dict['Profiles']['nameOrigin'].loc[db_dict['Profiles']['Profile Type'] == 'Energy use'].loc[db_dict['Profiles']['Day'] == 'Weekday']
        energy_listWE = db_dict['Profiles']['nameOrigin'].loc[db_dict['Profiles']['Profile Type'] == 'Energy use'].loc[db_dict['Profiles']['Day'] == 'Weekend']
        snow_listWD = db_dict['Profiles']['nameOrigin'].loc[db_dict['Profiles']['Profile Type'] == 'Snow removal'].loc[db_dict['Profiles']['Day'] == 'Weekday']
        snow_listWE = db_dict['Profiles']['nameOrigin'].loc[db_dict['Profiles']['Profile Type'] == 'Snow removal'].loc[db_dict['Profiles']['Day'] == 'Weekend']
        wateruse_listWD = db_dict['Profiles']['nameOrigin'].loc[db_dict['Profiles']['Profile Type'] == 'Water use (manual)'].loc[db_dict['Profiles']['Day'] == 'Weekday']
        wateruse_listWE = db_dict['Profiles']['nameOrigin'].loc[db_dict['Profiles']['Profile Type'] == 'Water use (manual)'].loc[db_dict['Profiles']['Day'] == 'Weekend']
        
        wateruse_listAWD = db_dict['Profiles']['nameOrigin'].loc[db_dict['Profiles']['Profile Type'] == 'Water use (automatic)'].loc[db_dict['Profiles']['Day'] == 'Weekday']
        wateruse_listAWE = db_dict['Profiles']['nameOrigin'].loc[db_dict['Profiles']['Profile Type'] == 'Water use (automatic)'].loc[db_dict['Profiles']['Day'] == 'Weekend']

        self.dlg.comboBoxPaved.addItems(sorted(paved_list))
        self.dlg.comboBoxBuilding.addItems(sorted(building_list))
        self.dlg.comboBoxBareSoil.addItems(sorted(bsoil_list))
        self.dlg.comboBoxGrass.addItems(sorted(grass_list))
        self.dlg.comboBoxEvrTree.addItems(sorted(evr_tree_list))
        self.dlg.comboBoxDecTree.addItems(sorted(dec_tree_list))
        self.dlg.comboBoxAnthro.addItems(sorted(anthro_list)) 
        self.dlg.comboBoxTrafficWD.addItems(sorted(traffic_listWD))
        self.dlg.comboBoxTrafficWE.addItems(sorted(traffic_listWE))
        self.dlg.comboBoxEnergyUseWD.addItems(sorted(energy_listWD))
        self.dlg.comboBoxEnergyUseWE.addItems(sorted(energy_listWE))
        self.dlg.comboBoxPopdensWD.addItems(sorted(popdens_listWD))
        self.dlg.comboBoxPopdensWE.addItems(sorted(popdens_listWE))
        self.dlg.comboBoxHumanWD.addItems(sorted(human_listWD))
        self.dlg.comboBoxHumanWE.addItems(sorted(human_listWE))
        self.dlg.comboBoxSnowWD.addItems(sorted(snow_listWD))
        self.dlg.comboBoxSnowWE.addItems(sorted(snow_listWE))
        self.dlg.comboBoxIrrigationWD.addItems(sorted(wateruse_listWD)) 
        self.dlg.comboBoxIrrigationWE.addItems(sorted(wateruse_listWE))
        self.dlg.comboBoxIrrigationAWD.addItems(sorted(wateruse_listAWD)) 
        self.dlg.comboBoxIrrigationAWE.addItems(sorted(wateruse_listAWE))
        

        for cbox in [self.dlg.comboBoxPaved, self.dlg.comboBoxBuilding, self.dlg.comboBoxBareSoil, self.dlg.comboBoxEvrTree, self.dlg.comboBoxDecTree, 
                     self.dlg.comboBoxGrass, self.dlg.comboBoxAnthro,self.dlg.comboBoxTrafficWD,self.dlg.comboBoxTrafficWE,
                     self.dlg.comboBoxSnowWD,self.dlg.comboBoxSnowWE,self.dlg.comboBoxHumanWD, self.dlg.comboBoxHumanWE,
                     self.dlg.comboBoxPopdensWD, self.dlg.comboBoxPopdensWE, self.dlg.comboBoxEnergyUseWD, self.dlg.comboBoxEnergyUseWE,
                     self.dlg.comboBoxIrrigationAWD,self.dlg.comboBoxIrrigationAWE, self.dlg.comboBoxIrrigationWD, self.dlg.comboBoxIrrigationWE
                     ]: #steg3
            cbox.setCurrentIndex(-1)

        self.dlg.comboBoxCountry.addItems(sorted(set(list(db_dict['Country']['nameOrigin']))))
        self.dlg.comboBoxCountry.setCurrentIndex(-1)
        
        self.dlg.comboBoxCountry.currentIndexChanged.connect(lambda: self.country_changed(db_dict))

        self.dlg.checkBoxTypology.stateChanged.connect(lambda: self.use_typologies())
        self.dlg.checkBox_day.stateChanged.connect(lambda: self.popdaystate())
        self.dlg.pop_density.currentIndexChanged.connect(lambda: self.set_pop_density())
        self.dlg.pop_density_day.currentIndexChanged.connect(lambda: self.set_pop_density_day())

        self.dlg.layerComboManagerPolygrid.setCurrentIndex(-1)
        self.dlg.layerComboManagerPolygrid.setFilters(QgsMapLayerProxyModel.Filter.PolygonLayer)
        self.dlg.layerComboManagerPolygrid.setFixedWidth(175)
        self.dlg.layerComboManagerPolyField.setFilters(QgsFieldProxyModel.Filter.LongLong)
        self.dlg.layerComboManagerPolygrid.layerChanged.connect(self.dlg.layerComboManagerPolyField.setLayer)

        # New for Typology database
        self.dlg.layerComboManagerDSM.setFilters(QgsMapLayerProxyModel.Filter.RasterLayer)
        self.dlg.layerComboManagerDSM.setFixedWidth(175)
        self.dlg.layerComboManagerDSM.setCurrentIndex(-1)
        self.dlg.layerComboManagerDEM.setFilters(QgsMapLayerProxyModel.Filter.RasterLayer)
        self.dlg.layerComboManagerDEM.setFixedWidth(175)
        self.dlg.layerComboManagerDEM.setCurrentIndex(-1)

        self.dlg.layerComboManagerPolygridTypo.setFilters(QgsMapLayerProxyModel.Filter.PolygonLayer)
        self.dlg.layerComboManagerPolygridTypo.setFixedWidth(175)
        self.dlg.layerComboManagerPolygridTypo.setCurrentIndex(-1)

        self.dlg.pop_density.setFilters(QgsFieldProxyModel.Filter.Numeric)
        self.dlg.layerComboManagerPolygrid.layerChanged.connect(self.dlg.pop_density.setLayer)

        self.dlg.pop_density_day.setFilters(QgsFieldProxyModel.Filter.Numeric)
        self.dlg.layerComboManagerPolygrid.layerChanged.connect(self.dlg.pop_density_day.setLayer)
    
        self.dlg.pushButtonImportLCF.clicked.connect(lambda: self.set_LCFfile_path())
        self.dlg.pushButtonImportIMPVeg.clicked.connect(lambda: self.set_IMPvegfile_path())

        self.dlg.checkBox_twovegfiles.clicked.connect(lambda: self.use_twovegfiles())
        self.dlg.pushButtonImportIMPVeg_eve.clicked.connect(lambda: self.set_IMPvegfile_path_eve())
        self.dlg.pushButtonImportIMPVeg_dec.clicked.connect(lambda: self.set_IMPvegfile_path_dec())

        self.dlg.pushButtonImportIMPBuild.clicked.connect(lambda: self.set_IMPfile_path())
        self.dlg.pushButtonImportMet.clicked.connect(lambda: self.set_metfile_path())

        self.dlg.spinBoxStartDLS.valueChanged.connect(lambda: self.start_DLS_changed(self.dlg.spinBoxStartDLS.value()))
        self.dlg.spinBoxEndDLS.valueChanged.connect(lambda: self.end_DLS_changed(self.dlg.spinBoxEndDLS.value()))

        self.dlg.spinBoxSoilMoisture.valueChanged.connect(lambda: self.soil_moisture_changed(self.dlg.spinBoxSoilMoisture.value()))
        self.dlg.comboBoxLeafCycle.currentIndexChanged.connect(lambda: self.leaf_cycle_changed(self.dlg.comboBoxLeafCycle.currentIndex()))
        self.dlg.fileCodeLineEdit.textChanged.connect(lambda: self.file_code_changed(self.dlg.fileCodeLineEdit.text()))
        self.dlg.lineEditUTC.textChanged.connect(lambda: self.utc_changed(self.dlg.lineEditUTC.text()))

        #SS related GUI things
        self.dlg.checkBoxSS.stateChanged.connect(lambda: self.use_spartacus())
        self.dlg.SS_comboBox.currentIndexChanged.connect(lambda: self.height_option_SS(self.dlg.SS_comboBox.currentIndex()))
        self.dlg.SS_LineEdit_constant.textChanged.connect(lambda: self.vertHeights_changed(self.dlg.SS_LineEdit_constant.text()))
        self.dlg.spinBoxLayers.valueChanged.connect(lambda: self.layersSS_changed(self.dlg.spinBoxLayers.value()))
        self.dlg.SS_checkBox_skew.stateChanged.connect(lambda: self.SS_skew())
        
        # New for traffic linedata
        self.dlg.layerComboManagerTraffic.setCurrentIndex(-1)
        self.dlg.layerComboManagerTraffic.setFilters(QgsMapLayerProxyModel.Filter.LineLayer)
        self.dlg.layerComboManagerTraffic.setFixedWidth(175)
        self.dlg.layerComboManagerTrafficField.setFilters(QgsFieldProxyModel.Filter.LongLong)
        self.dlg.layerComboManagerTraffic.layerChanged.connect(self.dlg.layerComboManagerTrafficField.setLayer)
        self.dlg.show()
        self.dlg.adjustSize()
        self.dlg.exec()

    def help(self):
        url = "https://umep-docs.readthedocs.io/en/latest/pre-processor/Urban%20Energy%20Balance%20SUEWS%20Database%20Prepare.html"
        webbrowser.open_new_tab(url)

    def set_output_folder(self):
        self.outputDialog.open()
        result = self.outputDialog.exec()
        if result == 1:
            self.output_dir = self.outputDialog.selectedFiles()
            self.dlg.textOutput.setText(self.output_dir[0])
            self.dlg.runButton.setEnabled(1)

    def height_option_SS(self, value):
        self.heightMethod = value

    def layersSS_changed(self, value):
        self.nlayers = value

    def vertHeights_changed(self, heights):      
        self.vertheights = heights

    def SS_skew(self):
        if self.dlg.SS_checkBox_skew.isChecked():
            self.skew = 2
        else:
            self.skew = 1

    def soil_moisture_changed(self, value):
        self.soil_moisture = value

    def utc_changed(self, index):
        self.utc = int(index)

    def leaf_cycle_changed(self, index):
        self.leaf_cycle = index

    def file_code_changed(self, code):
        self.file_code = code

    def country_changed(self, db_dict):
        country_sel = self.dlg.comboBoxCountry.currentText()
        reg_sel = db_dict['Country'].loc[db_dict['Country']['nameOrigin'] == country_sel]['Region'].item()
        # reg_sel = self.dlg.comboBoxRegion.currentText()

        self.country_str = country_sel
        self.region_str = reg_sel
        # Update region according to country if country is chosen before region
        self.dlg.lineEditRegInfo.setText(reg_sel)

        # Function to test if a parameter is found in Country. If not, the same parameter for Region is then selected.
            
        def decide_country_region(col, country_sel, reg_sel, comboBox):
            country_df = db_dict['Country'][db_dict['Country']['nameOrigin'] == country_sel]
            
            if str(country_df[col].item()) == 'nan':
                reg_df = db_dict['Region'][db_dict['Region']['Region'] == reg_sel]
                var = reg_df[col].item()
                var_text = db_dict[surf_df_dict[col]].loc[var, 'nameOrigin']
                cbox_list = [comboBox.itemText(i) for i in range(comboBox.count())]
                try:
                    indexer = cbox_list.index(var_text)
                except:
                    comboBox.addItems([var_text])
                    cbox_list = [comboBox.itemText(i) for i in range(comboBox.count())]
                    indexer = cbox_list.index(var_text)

            else:
                var = country_df[col].item()
                var_text = db_dict[surf_df_dict[col]].loc[var, 'nameOrigin']
                cbox_list = [comboBox.itemText(i) for i in range(comboBox.count())]
                try:
                    indexer = cbox_list.index(var_text)
                except:
                    comboBox.addItems([var_text])
                    cbox_list = [comboBox.itemText(i) for i in range(comboBox.count())]
                    indexer = cbox_list.index(var_text)
    
            comboBox.setCurrentIndex(indexer)

        decide_country_region('Paved',country_sel, reg_sel, self.dlg.comboBoxPaved)
        decide_country_region('Buildings', country_sel, reg_sel, self.dlg.comboBoxBuilding)
        decide_country_region('Bare Soil', country_sel, reg_sel, self.dlg.comboBoxBareSoil)
        decide_country_region('Evergreen Tree',country_sel, reg_sel, self.dlg.comboBoxEvrTree)
        decide_country_region('Deciduous Tree',country_sel, reg_sel, self.dlg.comboBoxDecTree)
        decide_country_region('Grass',country_sel, reg_sel, self.dlg.comboBoxGrass)
        decide_country_region('AnthropogenicCode',country_sel, reg_sel, self.dlg.comboBoxAnthro)
        decide_country_region('TraffProfWD',country_sel, reg_sel, self.dlg.comboBoxTrafficWD) #finns bara i country?
        decide_country_region('TraffProfWE',country_sel, reg_sel, self.dlg.comboBoxTrafficWE) 
        decide_country_region('ActivityProfWD',country_sel, reg_sel, self.dlg.comboBoxHumanWD)
        decide_country_region('ActivityProfWE',country_sel, reg_sel, self.dlg.comboBoxHumanWE)
        decide_country_region('PopProfWD',country_sel, reg_sel, self.dlg.comboBoxPopdensWD)
        decide_country_region('PopProfWE',country_sel, reg_sel, self.dlg.comboBoxPopdensWE)
        decide_country_region('EnergyUseProfWD',country_sel, reg_sel, self.dlg.comboBoxEnergyUseWD)
        decide_country_region('EnergyUseProfWE',country_sel, reg_sel, self.dlg.comboBoxEnergyUseWE)
        decide_country_region('WaterUseProfManuWD',country_sel, reg_sel, self.dlg.comboBoxIrrigationWD) #TODO Manual and Auto together?
        decide_country_region('WaterUseProfManuWE',country_sel, reg_sel, self.dlg.comboBoxIrrigationWE)
        decide_country_region('WaterUseProfAutoWD',country_sel, reg_sel, self.dlg.comboBoxIrrigationAWD) #TODO Manual and Auto together?
        decide_country_region('WaterUseProfAutoWE',country_sel, reg_sel, self.dlg.comboBoxIrrigationAWE)
        decide_country_region('SnowClearingProfWD',country_sel, reg_sel, self.dlg.comboBoxSnowWD)
        decide_country_region('SnowClearingProfWE',country_sel, reg_sel, self.dlg.comboBoxSnowWE)

    def use_typologies(self):
        if self.dlg.checkBoxTypology.isChecked():
            self.typologies = 1
        else:
            self.typologies = 0

    def use_spartacus(self):
        if self.dlg.checkBoxSS.isChecked():
            self.spartacus = 1
        else:
            self.spartacus = 0

    def use_twovegfiles(self):
        if self.dlg.checkBox_twovegfiles.isChecked():
            self.twovegfiles = 1
        else:
            self.twovegfiles = 0

    def popdaystate(self):
        if self.dlg.checkBox_day.isChecked():
            self.daypop = 1
        else:
            self.daypop = 0

    def set_pop_density(self):
            self.pop_density = self.dlg.pop_density.currentText()
    
    def set_pop_density_day(self):
        self.pop_density_day = self.dlg.pop_density_day.currentText()

    def set_LCFfile_path(self):
        self.fileDialogISO.open()
        result = self.fileDialogISO.exec()
        if result == 1:
            self.LCFfile_path = self.fileDialogISO.selectedFiles()
            self.dlg.textInputLCFData.setText(self.LCFfile_path[0])
        else:
            self.LCFfile_path = None
            self.dlg.textInputLCFData.setText('')

    def set_IMPfile_path(self):
        self.fileDialogISO.open()
        result = self.fileDialogISO.exec()
        if result == 1:
            self.IMPfile_path = self.fileDialogISO.selectedFiles()
            self.dlg.textInputIMPData.setText(self.IMPfile_path[0])
            self.ss_dir = os.path.dirname(self.IMPfile_path[0])
            self.dlg.textInputIMPDataSS.setText(self.ss_dir)
        else:
            self.IMPfile_path = None
            self.dlg.textInputIMPData.setText('')

    def set_IMPvegfile_path(self):
        self.fileDialogISO.open()
        result = self.fileDialogISO.exec()
        if result == 1:
            self.IMPvegfile_path = self.fileDialogISO.selectedFiles()
            self.dlg.textInputIMPVegData.setText(self.IMPvegfile_path[0])
        else:
            self.IMPvegfile_path = None
            self.dlg.textInputIMPVegData.setText('')


    def set_IMPvegfile_path_dec(self):
        self.fileDialogISO.open()
        result = self.fileDialogISO.exec()
        if result == 1:
            self.IMPvegfile_path_dec = self.fileDialogISO.selectedFiles()
            self.dlg.textInputIMPDecData.setText(self.IMPvegfile_path_dec[0])
        else:
            self.IMPvegfile_path_dec = None
            self.dlg.textInputIMPDecData.setText('')

    def set_IMPvegfile_path_eve(self):
        self.fileDialogISO.open()
        result = self.fileDialogISO.exec()
        if result == 1:
            self.IMPvegfile_path_eve = self.fileDialogISO.selectedFiles()
            self.dlg.textInputIMPEveData.setText(self.IMPvegfile_path_eve[0])
        else:
            self.IMPvegfile_path_eve = None
            self.dlg.textInputIMPEveData.setText('')

    def set_metfile_path(self):
        self.Metfile_path = self.fileDialog.getOpenFileName()
        self.dlg.textInputMetData.setText(self.Metfile_path[0])

    def start_DLS_changed(self, value):
        self.start_DLS = value

    def end_DLS_changed(self, value):
        self.end_DLS = value

    def generate(self):
        # First check that all parts of the interface have been filled in correclty
        # output dir
        if self.output_dir is None:
            QMessageBox.critical(self.dlg, "Error", "No output directory selected")
            return  

        # metfile
        year = None
        year2 = None

        if self.Metfile_path is None:
            QMessageBox.critical(self.dlg, "Error", "Meteorological data file has not been provided")
            return
            pass
        elif os.path.isfile(self.Metfile_path[0]):
            with open(self.Metfile_path[0]) as metfile:
                next(metfile)
                for line in metfile:
                    split = line.split()
                    if year == split[0]:
                        break
                    else:
                        if year2 == split[0]:
                            year = split[0]
                            break
                        elif year is None:
                            year = split[0]
                        else:
                            year2 = split[0]
        else:
            QMessageBox.critical(self.dlg, "Error", "Could not find the file containing meteorological data")
            return
            pass
        
        # # check polygon grid
        poly = self.dlg.layerComboManagerPolygrid.currentLayer()
            
        if poly is None:
           QMessageBox.critical(None, "Error", "No valid Polygon layer is selected")
           return
        
        poly_field = self.dlg.layerComboManagerPolyField.currentField()
        
        if poly_field == '':
            QMessageBox.critical(None, "Error", "An attribute field with unique fields must be selected")
            return

        vlayer = QgsVectorLayer(poly.source(), "polygon", "ogr")

        map_units = vlayer.crs().mapUnits()
        if not map_units == 0 or map_units == 1 or map_units == 2:
            QMessageBox.critical(self.dlg, "Error", "Could not identify the map units of the polygon layer coordinate "
                                 "reference system")
            return

        if self.dlg.pop_density.currentField() == '':
            QMessageBox.critical(None, "Error", "An attribute field including night-time population density (pp/ha) must be selected")
            return

        if self.daypop == 1:
            if self.dlg.pop_density_day.currentField() == '':
                QMessageBox.critical(None, "Error", "An attribute field including working population density (pp/ha) must be selected")
                return    

        # Leaf cycle
        if self.leaf_cycle == 0:
            QMessageBox.critical(self.dlg, "Error", "No leaf cycle period has been selected")
            return
        else:
            if not (self.leaf_cycle == 1 or self.leaf_cycle == 5):
                QMessageBox.critical(self.dlg,"Warning", "A transition period between Winter and Summer has been "
                                     "choosen. Preferably start the model run during Winter or Summer.")

        # Morphometric and land cover files
        if self.IMP_from_file:
            if self.IMPfile_path is None:
                QMessageBox.critical(None, "Error", "Building morphology file has not been provided")
                return
            if not os.path.isfile(self.IMPfile_path[0]):
                QMessageBox.critical(None, "Error", "Could not find the file containing building morphology")
                return
        if self.IMPveg_from_file:
            if self.IMPvegfile_path is None:
                QMessageBox.critical(None, "Error", "Vegetation morphology file has not been provided")
                return
            if not os.path.isfile(self.IMPvegfile_path[0]):
                QMessageBox.critical(None, "Error", "Could not find the file containing vegetation morphology")
                return
            
        if self.twovegfiles == 1:
            if self.IMPvegfile_path_eve is None:
                QMessageBox.critical(None, "Error", "Vegetation morphology file has not been provided")
                return
            if not os.path.isfile(self.IMPvegfile_path_eve[0]):
                QMessageBox.critical(None, "Error", "Could not find the file containing vegetation morphology")
                return
            if self.IMPvegfile_path_dec is None:
                QMessageBox.critical(None, "Error", "Vegetation morphology file has not been provided")
                return
            if not os.path.isfile(self.IMPvegfile_path_dec[0]):
                QMessageBox.critical(None, "Error", "Could not find the file containing vegetation morphology")
                return
            
        if self.LCF_from_file:
            if self.LCFfile_path is None:
                QMessageBox.critical(None, "Error", "Land cover fractions file has not been provided")
                return
            if not os.path.isfile(self.LCFfile_path[0]):
                QMessageBox.critical(None, "Error", "Could not find the file containing land cover fractions")
                return
        
        if self.file_code == '':
            QMessageBox.critical(None, "Error", "Specify a file code prefix")
            return

        # # DSM layer for aggegation
        dsmlayer = self.dlg.layerComboManagerDSM.currentLayer()
        if dsmlayer is None:
            QMessageBox.critical(self.dlg, "Error", "No valid ground and building DSM is selected")
            return

        demlayer = self.dlg.layerComboManagerDEM.currentLayer()
        if demlayer is None:
            QMessageBox.critical(self.dlg, "Error", "No valid DEM selected")
            return

        polyTypolayer = self.dlg.layerComboManagerPolygridTypo.currentLayer()
        if self.typologies == 1 and polyTypolayer is None:
            QMessageBox.critical(None, "Error", "No valid Urban typology polygon layer is selected")
            return   
        
        typologyFieldName = 'TypolID'

        # Spartacus check
        if self.spartacus == 1:
            if self.heightMethod == 0:
                QMessageBox.critical(self.dlg, "Error", "No method to set height intervals is specified")
                return
            elif self.heightMethod == 1:
                try:
                    listtext = self.vertheights.split(',')
                    listtext.insert(0, '0.0')
                    floats = [float(x) for x in listtext]
                except:
                    QMessageBox.critical(self.dlg, "Error", "One or more inputs in Fixed height [option 1] is non-numeric. "
                                        "Remember to use comma between numbers")
                    return
                start = -1.0
                self.vertheights = []
                for x in floats:
                    if x > start:
                        self.vertheights.append(x)
                        start = x
                    else:
                        QMessageBox.critical(self.dlg, "Error", "One or more inputs in Fixed height [option 1] is not increasing.")
                        return

        settings_dict = {
            
            'vlayer':vlayer, 'poly_field':poly_field, 'Metfile_path': self.Metfile_path, 'start_DLS': self.start_DLS, 
            'end_DLS': self.end_DLS, 'LCF_from_file' :self.LCF_from_file, 'LCFfile_path' : self.LCFfile_path, 'plugin_dir': self.plugin_dir, 'map_units': map_units, 
            'output_dir': self.output_dir, 'file_code' :self.file_code, 'utc':self.utc,
            'IMP_from_file' : self.IMP_from_file, 'IMPfile_path': self.IMPfile_path, 'IMP_z0' : self.IMP_z0, 'IMP_zd' : self.IMP_zd, 
            'IMP_fai': self.IMP_fai, 'IMPveg_from_file': self.IMPveg_from_file, 'IMPvegfile_path': self.IMPvegfile_path, 
            'IMPveg_fai_eve': self.IMPveg_fai_eve, 'IMPveg_fai_dec' : self.IMPveg_fai_dec, 'pop_density' : self.pop_density, 
            'twovegfiles' : self.twovegfiles, 'IMPvegfile_path_dec':  self.IMPvegfile_path_dec, 
            # Typology
            'IMPvegfile_path_eve' : self.IMPvegfile_path_eve, 'pop_density_day': self.pop_density_day, 'daypop' :self.daypop,
            'polyTypolayer' : polyTypolayer, 'typologyFieldName': typologyFieldName, 'dsmlayer': dsmlayer, 'demlayer' :demlayer, 
            'region_str' : self.region_str, 'country_str' : self.country_str, 'checkBoxTypologies' :self.typologies,                          
            # Spartacus 
            'heightMethod' : self.heightMethod,  'vertheights' : self.vertheights, 'nlayers' : self.nlayers, 
            'skew' : self.skew, 'ss_dir': self.ss_dir, 'spartacus': self.spartacus, 
            }

        # Add to settings_dict selected profiles and surfaces
        for cbox, var in zip(
            [self.dlg.comboBoxPaved, self.dlg.comboBoxBuilding,self.dlg.comboBoxBareSoil, self.dlg.comboBoxEvrTree, self.dlg.comboBoxDecTree, 
            self.dlg.comboBoxGrass, self.dlg.comboBoxAnthro,self.dlg.comboBoxTrafficWD,self.dlg.comboBoxTrafficWE,
            self.dlg.comboBoxSnowWD,self.dlg.comboBoxSnowWE,self.dlg.comboBoxHumanWD, self.dlg.comboBoxHumanWE,
            self.dlg.comboBoxPopdensWD, self.dlg.comboBoxPopdensWE, self.dlg.comboBoxEnergyUseWD, self.dlg.comboBoxEnergyUseWE,
            self.dlg.comboBoxIrrigationWD,self.dlg.comboBoxIrrigationWE, self.dlg.comboBoxIrrigationAWD,self.dlg.comboBoxIrrigationAWE],
        
            ['Paved' ,'Buildings', 'Bare Soil', 'Evergreen Tree', 'Deciduous Tree', 
            'Grass' ,'AnthropogenicCode', 'TraffProfWD'  , 'TraffProfWE',
            'SnowClearingProfWD','SnowClearingProfWE', 'ActivityProfWD','ActivityProfWE',
            'PopProfWD' , 'PopProfWE', 'EnergyUseProfWD', 'EnergyUseProfWE',
            'WaterUseProfManuWD', 'WaterUseProfManuWE','WaterUseProfAutoWD', 'WaterUseProfAutoWE']):
            
            settings_dict[var] =  cbox.currentText()
        
        # TODO     
        # #Here worker loop starts. We make function. Then it is easier to put in worker later
        self.generateSiteSelect(settings_dict)


    def generateSiteSelect(self, settings_dict): #yml-file
        
        plugin_dir = settings_dict['plugin_dir']
        output_dir = settings_dict ['output_dir']

        save_txt_folder = output_dir[0]+ '/'
        temp_folder = plugin_dir + '/tempdata'
        walls_raster_out = temp_folder + '/walls.tif'

        build_raster_out = temp_folder + '/buildings.tif'
        typo_raster_out = temp_folder + '/typoraster.tif'

        # Clean temp folder
        try:
            rmtree(temp_folder)
        except OSError as e:
            print ("Warning: %s - %s." % (e.filename, e.strerror))

        os.mkdir(temp_folder)

        #ss_dict = {}                 # SiteSelect Dict. This is the final dict where all parameters for each grid are found. 
        veg_dict = {}                # dict for populating and sorting Veg parameter
        nonVeg_dict = {}             # dict for populating and sorting NonVeg parameter
        country_conv_dict = {}       # Dict for getting selected Country parameters
        parameter_dict = {}          # Unknown TODO Describe
        spartacus_error_dict = {}    # Used to collect potential Spartacus error and present for user
       # Read DB
        db_dict = read_DB(self.db_path)
    
        # change stringnames to codes. bit annoying, but more effective.. 20241127
        gui_lookup_dict = GUI_lookup_dict(db_dict)
        for var, lookup in zip(
            ['Paved' ,'Buildings', 'Bare Soil', 'Evergreen Tree', 'Deciduous Tree', 
            'Grass' ,'AnthropogenicCode', 'TraffProfWD'  , 'TraffProfWE',
            'SnowClearingProfWD','SnowClearingProfWE', 'ActivityProfWD','ActivityProfWE',
            'PopProfWD' , 'PopProfWE', 'EnergyUseProfWD', 'EnergyUseProfWE',
            'WaterUseProfManuWD', 'WaterUseProfManuWE','WaterUseProfAutoWD', 'WaterUseProfAutoWE'],
            
            ['Paved' ,'Buildings', 'Bare Soil', 'Evergreen Tree', 'Deciduous Tree', 
            'Grass' ,'AnthropogenicEmission', 'Traffic'  , 'Traffic',
            'Snow removal','Snow removal', 'Human activity','Human activity',
            'Population density' , 'Population density', 'Energy use', 'Energy use',
            'Water use (manual)', 'Water use (manual)','Water use (automatic)', 'Water use (automatic)']):

            try:
                settings_dict[var] =  gui_lookup_dict[lookup][settings_dict[var]]
            except:
                  for lu in ['Paved' ,'Buildings', 'Bare Soil', 'Evergreen Tree', 'Deciduous Tree', 
                    'Grass' ,'AnthropogenicEmission', 'Traffic'  , 'Traffic',
                    'Snow removal','Snow removal', 'Human activity','Human activity',
                    'Population density' , 'Population density', 'Energy use', 'Energy use',
                    'Water use (manual)', 'Water use (manual)','Water use (automatic)', 'Water use (automatic)']:
                    try:
                        settings_dict[var] =  gui_lookup_dict[lu][settings_dict[var]]
                        break
                    except:
                        pass
        
        # Vectorize country_conv_dict creation
        country_conv_dict = (db_dict['Country']['Country'] + ', ' + db_dict['Country']['City']).to_dict()
        country_conv_dict_inv = {v: k for k, v in country_conv_dict.items()}

        # Get selected country
        country_sel = db_dict['Country'].loc[[country_conv_dict_inv[settings_dict['country_str']]]]

        # Drop irrelevant columns
        column_list = db_dict['Country'].drop(columns=['Region', 'Country', 'City']).columns

        # Set parameters from regional or country level and populate parameter_dict
        parameter_dict = {column: decide_country_or_region(column, country_sel, db_dict['Region']) for column in column_list}

        if settings_dict['LCF_from_file']:
            LCF_dict = read_morph_txt(settings_dict['LCFfile_path'][0])

        if settings_dict['IMP_from_file']:
            IMP_dict = read_morph_txt(settings_dict['IMPfile_path'][0])

        if settings_dict['IMPveg_from_file']:
            IMPveg_dict = read_morph_txt(settings_dict['IMPvegfile_path'][0])

        if settings_dict['twovegfiles'] == 1:
            IMPveg_dict_dec = read_morph_txt(settings_dict['IMPvegfile_path_dec'][0])
            IMPveg_dict_eve = read_morph_txt(settings_dict['IMPvegfile_path_eve'][0])

        
        geodata_output = {} # Dict for storing the output of the QGIS geodata processes

        # New approach 20240219. Create typologylayer if no typoloayer exist
        if settings_dict['checkBoxTypologies'] == 0:
            parin = {'INPUT':settings_dict['vlayer'],
                'FIELD':[],
                'SEPARATE_DISJOINT':False,
                'OUTPUT':temp_folder + '/defaultTypo.shp'}
            geodata_output['defaulttypo'] = processing.run("native:dissolve", parin )

            # defaultTypoID = db_dict['NonVeg'].loc[db_dict['NonVeg']['nameOrigin']==defTypo].index.item()
            
            defaultTypoID = create_code('Types')
            db_dict['Types'].loc[defaultTypoID, 'Paved'] = settings_dict['Paved']#db_dict['NonVeg'][(db_dict['NonVeg']['nameOrigin'] == settings_dict['Paved']) & (db_dict['NonVeg']['Surface'] == 'Paved')].index.item()
            db_dict['Types'].loc[defaultTypoID, 'Buildings'] = settings_dict['Buildings']#db_dict['NonVeg'][(db_dict['NonVeg']['nameOrigin'] == settings_dict['Buildings']) & (db_dict['NonVeg']['Surface'] == 'Buildings')].index.item()

            parin = {'INPUT':geodata_output['defaulttypo']['OUTPUT'],
                     'FIELD_NAME':'TypolID',
                     'FIELD_TYPE':1,'FIELD_LENGTH':0,'FIELD_PRECISION':0,
                     'FORMULA': str(defaultTypoID),
                     'OUTPUT':temp_folder + '/defaultTypo2.shp'}

            geodata_output['defaulttypo2'] = processing.run("native:fieldcalculator", parin)

            settings_dict['polyTypolayer'] = QgsVectorLayer(geodata_output['defaulttypo2']['OUTPUT'],'typo','ogr')


        # If using Typologies then aggregation is needed. Otherwise no aggregation will be done TODO: ta bort if??
        #if checkBoxTypologies == 1:
        # DEM & DSM to arrays
        gdal.AllRegister()
        provider = settings_dict['demlayer'].dataProvider()
        filePath_dem = str(provider.dataSourceUri())
        dem = gdal.Open(filePath_dem)
        dem_arr = dem.ReadAsArray().astype(float)

        provider = settings_dict['dsmlayer'].dataProvider()
        filePath_dsm = str(provider.dataSourceUri())
        dsm = gdal.Open(filePath_dsm)
        dsm_arr = dsm.ReadAsArray().astype(float)

        #isolate buildings in dsm to be able to calculate mean height
        build_arr = dsm_arr-dem_arr
        build_arrW  = build_arr.copy()
        build_arr[np.where(build_arr < 0.5)] = np.nan
        saveraster(gdal.Open(filePath_dsm), build_raster_out, build_arr)
        # dsm_arr = None
        # dem_arr = None
        pixelSize = dsm.GetGeoTransform()[1]

        # Grid classified shp-file containing SUEWS typologies
        parin = { 'INPUT' : settings_dict['vlayer'],
            'INPUT_FIELDS' : [], 
            'OUTPUT' : temp_folder + '/urbantypelayer.shp', # 'TEMPORARY_OUTPUT',#urbantypelayer,
            'OVERLAY' : settings_dict['polyTypolayer'], 
            'OVERLAY_FIELDS' : [], 
            'OVERLAY_FIELDS_PREFIX' : '' }
        geodata_output['gridded_shp'] = processing.run('native:intersection', parin)

        # Dissolve on GridID and Typology. 
        parin = {
            'INPUT':geodata_output['gridded_shp']['OUTPUT'], 
            'FIELD':['ID',settings_dict['typologyFieldName']],
            'SEPARATE_DISJOINT':False,
            'OUTPUT': temp_folder + '/urbantypelayer_diss.shp'}
        geodata_output['gridded_shp_diss'] = processing.run("native:dissolve", parin)

        ####################################################################################
        ################# Start calculating volumetric fractions ###########################
        ####################################################################################

        # This dictionary retrieve the Code for selected typologies. The Reclassifier uses String values, but later on we need codes in Int

        if settings_dict['spartacus'] == 1:
            # Create wall raster

            # New findwalls from 20241022 
            walls = findwalls(build_arrW, 0.5) # feedback, total) # 0.5 meter difference in kernel filter identify a wall
            saveraster(gdal.Open(filePath_dsm), walls_raster_out, walls)

            # find prefix for filename in IMP-file
            pre = os.path.basename(settings_dict['IMPfile_path'][0])[:os.path.basename(settings_dict['IMPfile_path'][0]).find('_')]

            # new approach using rasterize instead of zonal stat to avoid shapefile overwrite issue in QGIS
            ras_crs = osr.SpatialReference()
            dsm_ref = settings_dict['dsmlayer'].crs().toWkt()
            ras_crs.ImportFromWkt(dsm_ref)
            rasEPSG = ras_crs.GetAttrValue("PROJCS|AUTHORITY", 1)
            geoTransform = dsm.GetGeoTransform()
            minx = geoTransform[0]
            maxy = geoTransform[3]
            maxx = minx + geoTransform[1] * dsm.RasterXSize
            miny = maxy + geoTransform[5] * dsm.RasterYSize

            projwin = str(minx) + ',' + str(maxx) + ',' + str(miny) +',' + str(maxy) + ' [EPSG:' + str(rasEPSG) + ']'
            parin = {
                'INPUT':settings_dict['polyTypolayer'].source(),
                'FIELD':'TypolID',
                'BURN':0,
                'USE_Z':False,
                'UNITS':1, # Georeferenced units
                'WIDTH':pixelSize, 
                'HEIGHT':pixelSize,
                'EXTENT':projwin, 
                'NODATA':0,'OPTIONS':'','DATA_TYPE':4,'INIT':None,'INVERT':False,'EXTRA':'',  # datatyp 5 is wrong?
                'OUTPUT': typo_raster_out
                }
            processing.run("gdal:rasterize", parin)

        ## Calculate total fractions for nonSS runs ##
        # Zonal statstics: mean height of buildings grid/typology
        parin = {
            'INPUT': geodata_output['gridded_shp_diss']['OUTPUT'],
            'INPUT_RASTER':build_raster_out,
            'RASTER_BAND':1,
            'COLUMN_PREFIX':'z_',
            'STATISTICS':[2,4], # 2 mean, 4 = stdev
            'OUTPUT':'TEMPORARY_OUTPUT'}

        # output_name = 'build_count' # or perhaps something like this? + str(int(min_height)) + '_' + str(int(max_height)). now, it overwrites
        geodata_output['mean_build_height'] = processing.run("native:zonalstatisticsfb", parin) 

        # Zonal statstics: Count pixels within each grid/typology
        parin = {
            'INPUT': geodata_output['mean_build_height']['OUTPUT'],
            'INPUT_RASTER':build_raster_out,
            'RASTER_BAND':1,
            'COLUMN_PREFIX':'pixel_',
            'STATISTICS':[0], # Count
            'OUTPUT': 'TEMPORARY_OUTPUT'}

        geodata_output['mean_build_height_pixel_count'] = processing.run("native:zonalstatisticsfb", parin)

        cols = [f.name() for f in geodata_output['mean_build_height_pixel_count']['OUTPUT'].fields()]
        datagen = ([f[col] for col in cols] for f in geodata_output['mean_build_height_pixel_count']['OUTPUT'].getFeatures())

        df_build_frac = pd.DataFrame.from_records(data=datagen, columns=cols)
        df_build_frac = df_build_frac.set_index('ID')  # Set ID as index in df to be able to slice in df 

        df_build_frac['pixel_count'] = df_build_frac['pixel_count'] * pixelSize         
        df_build_frac['volume'] = df_build_frac['pixel_count'] * df_build_frac['z_mean'] 

        df_build_frac = df_build_frac.fillna(0)

        grid_dict = {}       # Dict that holds information on general typologies, uvalues, albedos and emmissivites and fractions
        gridlayoutOut = {}   # Dict that holds information on fractions of, albedos, emmissivitiees and uvalues for GridLaytout.nml
        dir_poly = plugin_dir + '/tempdata/poly_temp.shp' # move later

        if settings_dict['checkBoxTypologies'] == 0:
            QgsProject.instance().removeMapLayer(settings_dict['polyTypolayer'].source())
            del settings_dict['polyTypolayer']

        # loop through each grid (Typologies)
        self.dlg.progressBar.setMaximum(settings_dict['vlayer'].featureCount())
        index = 0
        for f in settings_dict['vlayer'].getFeatures(): 
            id = int(f.attribute(settings_dict['poly_field']))
            print('Processing ID for aggregation: ' + str(id))
            index = index + 1
            self.dlg.progressBar.setValue(index)
            
            # Create new key for each grid_id
            grid_dict[id] = {}
            nonVeg_dict[id] = {}
            gridlayoutOut[id] = {} 
            df_build_frac.loc[id, 'tot_fraction'] = df_build_frac.loc[id, 'volume'] / df_build_frac.loc[id,'volume'].sum()

            # get uvalues etc. from database and fractions from previous fraction calculations
            # TODO Make into function
            for row in df_build_frac.loc[[id]].iterrows():
                typology = int(row[1][settings_dict['typologyFieldName']]) 
                fraction = row[1]['tot_fraction']   
                grid_dict[id][typology] = {}
                grid_dict[id][typology]['SAreaFrac'] = fraction # populate fractions in grid_dict

                # Use locator to avoid this long and complicated pandas slice at all below rows
                # locator selects the seleced 'Spartacus Surface' from the Spartacus surface connected to the building in the selected typology
                locator =  db_dict['Spartacus Surface'].loc[db_dict['NonVeg'].loc[db_dict['Types'].loc[typology]['Buildings']]['Spartacus Surface']]
                grid_dict[id][typology]['uvalue_wall'] = locator['u_value_wall']
                grid_dict[id][typology]['uvalue_roof'] = locator['u_value_roof']
                grid_dict[id][typology]['albedo_roof'] = locator['albedo_roof']
                grid_dict[id][typology]['albedo_wall'] = locator['albedo_wall']
                grid_dict[id][typology]['emissivity_roof'] = locator['emissivity_roof']
                grid_dict[id][typology]['emissivity_wall'] = locator['emissivity_wall']
            
            ## Spartacus ##
            if settings_dict['spartacus'] == 1:
                #vertical info from IMP calc
                ssVect  = pd.read_csv(settings_dict['ss_dir'] + '/' + pre + '_IMPGrid_SS_' + str(id) + '.txt', sep='\s+')

                # Some error check before calculation of gridlayout
                error_output = {}
                vertHeightsIn = settings_dict['vertheights']
                if settings_dict['heightMethod'] == 1: # static levels (taken from interface). Last value > max height
                    if ssVect['z'].max() <= max(vertHeightsIn):
                        error_output = {id : f"zMax ({str(ssVect['z'].max())}) is lower than max fixed height  {str(max(vertHeightsIn))}."} #issue 773
                        print('error in ID: ', str(id), f". zMax is lower than max fixed height {str(max(vertHeightsIn))}.")
                        QMessageBox.critical(None, "Error in Vertical Morphology Spartcus (opt 1)", 'error in ID: ' + str(id) + '. zMax (' + str(ssVect['z'].max()) + 
                                     ') lower than max fixed height ' + str(max(vertHeightsIn)) + '. Use other method or reduce fixed layer height.')
                        self.dlg.progressBar.setValue(index)
                        return

                elif settings_dict['heightMethod'] == 2: # always nlayers layer based on percentiles
                    nlayerOut = settings_dict['nlayers']
                    if ssVect['z'].max() <= nlayerOut:
                        error_output = {id : f"zMax ({str(ssVect['z'].max())}) is to low to use {str(nlayerOut)} vertical layers."} #issue 773
                        print('error in ID: ', str(id), f". zMax is to low to use {str(nlayerOut)} vertical layers.")
                        QMessageBox.critical(None, "Error in Vertical Morphology Spartcus (opt 2)", 'error in ID: ' + str(id) + '. zMax (' + str(ssVect['z'].max()) + 
                                     ') is to low when using ' + str(nlayerOut) + ' vertical layers. Use other method or reduce number of vertical layers.')
                        self.dlg.progressBar.setValue(index)
                        return

                # Determinte height intervals and number of vertical layers in current grid
                heightIntervals, nlayer = getVertheights(
                    ssVect, 
                    settings_dict['heightMethod'], 
                    copy.deepcopy(settings_dict['vertheights']), 
                    settings_dict['nlayers'], 
                    settings_dict['skew'], 
                    id)
                
                # Add errors (if any) to spartacus error dict used later for
                if error_output:        
                    spartacus_error_dict = spartacus_error_dict | error_output

                gridlayoutOut[id]['nlayer'] = nlayer
                gridlayoutOut[id]['height'] = heightIntervals.copy()

                #Access grid geometry
                prov = settings_dict['vlayer'].dataProvider()
                fields = prov.fields()
                attributes = f.attributes()
                geometry = f.geometry()
                feature = QgsFeature()
                feature.setAttributes(attributes)
                feature.setGeometry(geometry)
                writer = QgsVectorFileWriter(dir_poly, "CP1250", fields, prov.wkbType(), prov.crs(), "ESRI shapefile")
                writer.addFeature(feature)
                del writer

                #Clip building, wall and typology raster 
                bigraster = gdal.Open(build_raster_out)
                clip_spec = gdal.WarpOptions(format="GTiff", cutlineDSName=dir_poly, cropToCutline=True)
                gdal.Warp(temp_folder + '/clipbuild.tif', bigraster, options=clip_spec)
                dataset = gdal.Open(temp_folder + '/clipbuild.tif')
                build_array = dataset.ReadAsArray().astype(float)
                bigraster = None
                dataset = None
                
                bigraster = gdal.Open(walls_raster_out)
                clip_spec = gdal.WarpOptions(format="GTiff", cutlineDSName=dir_poly, cropToCutline=True)
                gdal.Warp(temp_folder + '/clipwall.tif', bigraster, options=clip_spec)
                dataset = gdal.Open(temp_folder + '/clipwall.tif')
                wall_array = dataset.ReadAsArray().astype(float)
                bigraster = None
                dataset = None

                bigraster = gdal.Open(typo_raster_out)
                clip_spec = gdal.WarpOptions(format="GTiff", cutlineDSName=dir_poly, cropToCutline=True)
                gdal.Warp(temp_folder + '/cliptypo.tif', bigraster, options=clip_spec)
                dataset = gdal.Open(temp_folder + '/cliptypo.tif')
                typo_array = dataset.ReadAsArray().astype(float)
                bigraster = None
                dataset = None

                # clip dem
                clip_spec = gdal.WarpOptions(format="GTiff", cutlineDSName=dir_poly, cropToCutline=True)
                gdal.Warp(temp_folder + '/clipdem.tif', dem, options=clip_spec)
                dataset = gdal.Open(temp_folder + '/clipdem.tif')
                dem_clip_array = dataset.ReadAsArray().astype(float)
                dataset = None

                # clip dsm
                clip_spec = gdal.WarpOptions(format="GTiff", cutlineDSName=dir_poly, cropToCutline=True)
                gdal.Warp(temp_folder + '/clipdsm.tif', dsm, options=clip_spec)
                dataset = gdal.Open(temp_folder + '/clipdsm.tif')
                dsm_clip_array = dataset.ReadAsArray().astype(float)        
                dataset = None

                buildings_count = count_buildings(dsm_clip_array, dem_clip_array) 
     
                gridlayoutOut[id]['wallAreaGrid'] = wall_array.sum() * pixelSize
                gridlayoutOut[id]['buildings_count'] = buildings_count

                typoList = np.unique(typo_array)

            else:

                #Access grid geometry
                prov = settings_dict['vlayer'].dataProvider()
                fields = prov.fields()
                attributes = f.attributes()
                geometry = f.geometry()
                feature = QgsFeature()
                feature.setAttributes(attributes)
                feature.setGeometry(geometry)
                writer = QgsVectorFileWriter(dir_poly, "CP1250", fields, prov.wkbType(), prov.crs(), "ESRI shapefile")
                writer.addFeature(feature)
                del writer

                # clip dem
                clip_spec = gdal.WarpOptions(format="GTiff", cutlineDSName=dir_poly, cropToCutline=True)
                gdal.Warp(temp_folder + '/clipdem.tif', dem, options=clip_spec)
                dataset = gdal.Open(temp_folder + '/clipdem.tif')
                dem_clip_array = dataset.ReadAsArray().astype(float)
                dataset = None

                # clip dsm
                clip_spec = gdal.WarpOptions(format="GTiff", cutlineDSName=dir_poly, cropToCutline=True)
                gdal.Warp(temp_folder + '/clipdsm.tif', dsm, options=clip_spec)
                dataset = gdal.Open(temp_folder + '/clipdsm.tif')
                dsm_clip_array = dataset.ReadAsArray().astype(float)        
                dataset = None

                buildings_count = count_buildings(dsm_clip_array, dem_clip_array) 
                gridlayoutOut[id]['buildings_count'] = buildings_count


            # Make a list to count if thera are more than one typology in grid. Test in the if statement below
            typology_list = list(grid_dict[id].keys())

            # Check if aggregation is needed
            # Now we blend typologies and aggregate for new codes
            if len(typology_list) > 1:
                nonVeg_dict[id]['Paved']     = blend_SUEWS_NonVeg(grid_dict[id], db_dict, parameter_dict, zenodo, 'Paved') 
                nonVeg_dict[id]['Buildings'] = blend_SUEWS_NonVeg(grid_dict[id], db_dict, parameter_dict, zenodo, 'Buildings') 

            # if only one typology exists, no need to aggregate/combine/blend
            else:
                if settings_dict['checkBoxTypologies'] == 0:

                    nonVeg_dict[id]['Paved'] = fill_SUEWS_NonVeg_typologies(db_dict, parameter_dict, settings_dict['Paved'], zenodo, 'Paved')
                    nonVeg_dict[id]['Buildings'] = fill_SUEWS_NonVeg_typologies( db_dict, parameter_dict, settings_dict['Buildings'], zenodo, 'Buildings')
                else:
                    nonVeg_dict[id]['Paved'] = fill_SUEWS_NonVeg_typologies(db_dict, parameter_dict, settings_dict['Paved'], zenodo, 'Paved')
                    nonVeg_dict[id]['Buildings'] = fill_SUEWS_NonVeg_typologies( db_dict, parameter_dict, settings_dict['Buildings'], zenodo, 'Buildings')
                
            nonVeg_dict[id]['Bare Soil'] = fill_SUEWS_NonVeg_typologies(db_dict, parameter_dict, settings_dict['Bare Soil'], zenodo, 'Bare Soil')
            gridlayoutOut[id]['vertical_layers'] = {}
            
            if settings_dict['spartacus'] == 1:
                gridlayoutOut[id]['vertical_layers'] = ss_calc_gridlayout(build_array, wall_array, typoList, typo_array,  gridlayoutOut, id, db_dict, zenodo, settings_dict['ss_dir'], pre, grid_dict[id]),

        # write to SUEWS_NonVeg
           
        # Write SUEWS.txt files SUEWS_veg, SUEWS_AnthropogenicEmission, SUEWS_Water and SUEWS_Conductance
        veg_dict = fill_SUEWS_Veg(db_dict, settings_dict, parameter_dict,  zenodo)
    
        # Save Profiles
        profiles_dict = profiles_to_dict(settings_dict, db_dict['Profiles'], db_dict['References'])

        # set correct values and write txt.files based on the parameters found at country/regional level 
        snow_dict = fill_SUEWS_Snow(parameter_dict['SnowCode'], db_dict, zenodo)
        water_dict = fill_SUEWS_Water(parameter_dict['Water'], db_dict, parameter_dict, zenodo)

        cond_dict = db_dict['Conductance'].loc[parameter_dict['Conductance']].to_dict()
        cond_dict['Code'] = parameter_dict['Conductance']

                
        leaf_cycle = self.leaf_cycle


        # ################################################################################################################################
        #                                               Writing SiteSelect.txt  (now, yaml)     
        # ################################################################################################################################

        if settings_dict['Metfile_path'] is None:
            QMessageBox.critical(None, "Error", "Meteorological data file has not been provided,"
                                        " please check the main tab")
            return
        else:
            # Define the column names (you can adjust this to match the actual met forcing file)
            column_names = [
                'iy', 'id', 'it', 'imin', 'qn', 'qh', 'qe', 'qs', 'qf', 'U', 'RH', 'Td', 'press', 'rain',
                'kdown', 'snow', 'ldown', 'fcld', 'wuh', 'xsmd', 'lai', 'kdiff', 'kdir', 'wdir'
            ]

            df = pd.read_csv(settings_dict['Metfile_path'][0], sep=r"\s+", skiprows=1, names=column_names)

            # 2. Construct datetime index from columns: iy (year), id (DOY), it (hour), imin (minute)
            df['datetime'] = pd.to_datetime(df['iy'], format='%Y') + pd.to_timedelta(df['id'] - 1, unit='D') + \
                            pd.to_timedelta(df['it'], unit='h') + pd.to_timedelta(df['imin'], unit='m')
            df.set_index('datetime', inplace=True)

            # 3. Compute time resolution in minutes
            dectime0 = df['iy'].iloc[0] + df['it'].iloc[0] / 24 + df['imin'].iloc[0] / (60 * 24)
            dectime1 = df['iy'].iloc[1] + df['it'].iloc[1] / 24 + df['imin'].iloc[1] / (60 * 24)
            res = int(round((dectime1 - dectime0) * 60 * 24))

            # Get start and end date
            start_date = df.index[0].strftime('%Y-%m-%d')
            end_date = df.index[-1].strftime('%Y-%m-%d')

            # 4. Define output path
            YYYY = int(df['iy'].min())
            data_out = f'{self.output_dir[0]}/{self.file_code}_{YYYY}_data_{res}.txt'

            # 5. Save the file using .to_csv (formatted like np.savetxt)
            df_out = df[column_names]  # Preserve original order
            header = 'iy id it imin Q* QH QE Qs Qf Wind RH Td press rain Kdn snow ldown fcld wuh xsmd lai_hr ' \
                    'Kdiff Kdir Wd'

            # Save with formatting
            np.savetxt(data_out, df_out.to_numpy(), fmt='%3d %2d %3d %2d %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f '
                        '%6.2f %6.2f %6.2f %6.4f %6.2f %6.2f %6.2f %6.2f %6.4f %6.2f %6.2f %6.2f %6.2f %6.2f',
                        delimiter=' ', header=header, comments='')

        with open(self.supylib + '/sample_data/sample_config.yml', 'r') as f:
            yaml_dict = yaml.load(f, Loader=yaml.SafeLoader)


        # Start populating yml-file
        yaml_dict['model']['control']['forcing_file']['value'] = data_out
        yaml_dict['model']['control']['forcing_file']['value'] = data_out
        yaml_dict['model']['control']['start_time'] = start_date
        yaml_dict['model']['control']['end_time'] = end_date
        
        # yaml_dict['model']['physics']
        # Comment out lines that only contains default values here. This is set later in Processing/SUEWS
        # yaml_dict['model']['physics']['netradiationmethod']['value'] = 1 #changed from 1 FL
        yaml_dict['model']['physics']['emissionsmethod']['value'] = db_dict['AnthropogenicEmission'].loc[parameter_dict['AnthropogenicCode'],'Model']
        # yaml_dict['model']['physics']['storageheatmethod']['value'] = 1 #Changed from 3 FL
        # yaml_dict['model']['physics']['ohmincqf']['value'] = 0
        # yaml_dict['model']['physics']['roughlenmommethod']['value'] = 1 # Values based on Kent et al. 2017
        # yaml_dict['model']['physics']['roughlenheatmethod']['value'] = 2
        #['model']['physics']['stabilitymethod']['value'] = 3
        #yaml_dict['model']['physics']['smdmethod']['value'] = 0
        #yaml_dict['model']['physics']['waterusemethod']['value'] = 0
        # yaml_dict['model']['physics']['rslmethod']['value'] = 2
        # yaml_dict['model']['physics']['rsllevel']['value'] = 1
        #yaml_dict['model']['physics']['faimethod']['value'] = 0
        # yaml_dict['model']['physics']['localclimatemethod']['value'] = 0
        #yaml_dict['model']['physics']['snowuse']['value'] = 0
        # yaml_dict['model']['physics']['stebbsmethod']['value'] = 0
        #yaml_dict['model']['physics']['gsmodel']['value'] = cond_dict['gsModel']

        # Crete a default template of sites to poulate in loop
        base_grid_dict = yaml_dict['sites'][0].copy()

        # Delete sample entry of sites to populate later on
        del yaml_dict['sites'][0]

        # Loop Start for each Grid
        for feature in settings_dict['vlayer'].getFeatures():
            
            # temp_grid is the yaml_dict key that is used for each grid
            temp_grid = copy.deepcopy(base_grid_dict)
            feat_id = int(feature.attribute(settings_dict['poly_field']))
            temp_grid['name'] = f'grid no: {str(feat_id)}'
            temp_grid['gridiv'] = feat_id
    
            print('Processing ID: ' + str(feat_id) + ' for grid specific parameters')

            old_cs = osr.SpatialReference()
            vlayer_ref = settings_dict['vlayer'].crs().toWkt()
            old_cs.ImportFromWkt(vlayer_ref)

            wgs84_wkt = """
            GEOGCS["WGS 84",
                DATUM["WGS_1984",
                    SPHEROID["WGS 84",6378137,298.257223563,
                        AUTHORITY["EPSG","7030"]],
                    AUTHORITY["EPSG","6326"]],
                PRIMEM["Greenwich",0,
                    AUTHORITY["EPSG","8901"]],
                UNIT["degree",0.01745329251994328,
                    AUTHORITY["EPSG","9122"]],
                AUTHORITY["EPSG","4326"]]"""

            new_cs = osr.SpatialReference()
            new_cs.ImportFromWkt(wgs84_wkt)

            transform = osr.CoordinateTransformation(old_cs, new_cs)

            centroid = feature.geometry().centroid().asPoint()
            area = feature.geometry().area()

            # TODO Check so that this is m2 not hectare and change to more proper naming
            settings_dict['map_units'] = 0 # OB 200825 hardcoded to set m2 
            if settings_dict['map_units'] == 0:
                hectare = area # meter
            elif settings_dict['map_units'] == 1:
                hectare = area / 107640. # square foot
            else:
                hectare = area

            lonlat = transform.TransformPoint(centroid.x(), centroid.y())
            
            altitude = 1  #This is used for sun position calculations and have very small effect.

            if settings_dict['LCF_from_file']:
                LCF_paved     = LCF_dict[feat_id]['Paved']
                LCF_buildings = LCF_dict[feat_id]['Buildings']
                LCF_evergreen = LCF_dict[feat_id]['EvergreenTrees']
                LCF_decidious = LCF_dict[feat_id]['DecidiousTrees']
                LCF_grass     = LCF_dict[feat_id]['Grass']
                LCF_baresoil  = LCF_dict[feat_id]['Baresoil']
                LCF_water     = LCF_dict[feat_id]['Water']

            # Fraction of surface area that can be irrigated
            irrFr_EveTr = 0 
            irrFr_DecTr = 0
            irrFr_Grass = 0
            IrrFr_Bldgs = 0
            IrrFr_Paved = 0
            IrrFr_Water = 0
            IrrFr_BSoil = 0

            # TrafficRate_WD = 0.0135 # Already in dict
            # TrafficRate_WE = 0.0095 # Already in dict
            # QF0_BEU_WD = 0.88 # Already in dict
            # QF0_BEU_WE = 0.88 # Already in dict
           
            if settings_dict['IMP_from_file']:
                IMP_heights_mean = IMP_dict[feat_id]['zH']
                IMP_z0 = IMP_dict[feat_id]['z0']
                IMP_zd = IMP_dict[feat_id]['zd']
                IMP_fai = IMP_dict[feat_id]['fai']
                IMP_max = IMP_dict[feat_id]['zHmax']
                IMP_sd = IMP_dict[feat_id]['zHstd']
                IMP_wai = IMP_dict[feat_id]['wai']

            if settings_dict['twovegfiles'] == 1:
                IMPveg_heights_mean_eve = IMPveg_dict_eve[feat_id]['zH']
                IMPveg_heights_mean_dec = IMPveg_dict_dec[feat_id]['zH']
                IMPveg_fai_eve = IMPveg_dict_eve[feat_id]['fai']
                IMPveg_fai_dec = IMPveg_dict_dec[feat_id]['fai']
                IMPveg_max_eve = IMPveg_dict_eve[feat_id]['zHmax']  #TODO not used yet
                IMPveg_sd_eve = IMPveg_dict_eve[feat_id]['zHmax']  #TODO not used yet
                IMPveg_max_dec = IMPveg_dict_dec[feat_id]['zHstd']
                IMPveg_sd_dec = IMPveg_dict_dec[feat_id]['zHstd']                
            elif settings_dict['IMPveg_from_file']:
                IMPveg_heights_mean_eve = IMPveg_dict[feat_id]['zH']
                IMPveg_heights_mean_dec = IMPveg_dict[feat_id]['zH']
                IMPveg_fai_eve = IMPveg_dict[feat_id]['fai']
                IMPveg_fai_dec = IMPveg_dict[feat_id]['fai']
                IMPveg_max_eve = IMPveg_dict[feat_id]['zHmax']  #TODO not used yet
                IMPveg_sd_eve = IMPveg_dict[feat_id]['zHmax']  #TODO not used yet
                IMPveg_max_dec = IMPveg_dict[feat_id]['zHstd']
                IMPveg_sd_dec = IMPveg_dict[feat_id]['zHstd']

            # New calcualtion of rouhgness params v2017 (Kent et al. 2017b)
            # Evergreen not yet included in the calculations
            LCF_de = float(LCF_decidious)
            LCF_ev = float(LCF_evergreen)
            LCF_bu = float(LCF_buildings)
            LCF_tr = LCF_de + LCF_ev # temporary fix while ev and de is not separated, issue 155
            if (LCF_de  == 0 and LCF_ev == 0 and LCF_bu == 0):
                zH = 0
                zMax = 0
            else:
                zH = (float(IMP_heights_mean) * LCF_bu + float(IMPveg_heights_mean_eve) * LCF_ev + float(IMPveg_heights_mean_dec) * LCF_de) / (LCF_bu + LCF_ev + LCF_de)                    
                zMax = max(float(IMPveg_max_dec),float(IMP_max))

            if (LCF_de  == 0 and LCF_ev == 0 and LCF_bu == 0):
                sdComb = 0
                IMP_z0 = 0
                IMP_zd = 0
                # sdTree = np.sqrt((IMPveg_sd_eve ^ 2 / LCF_evergreen * area) + (IMPveg_sd_dec ^ 2 / LCF_decidious * area))  # not used yet
            elif (LCF_tr == 0 and LCF_bu != 0):
                sdComb = np.sqrt(float(IMP_sd) ** 2. / (LCF_bu * float(area)))  # Fix (fLCF_bu) issue #162
            elif (LCF_tr != 0 and LCF_bu == 0):
                sdComb = np.sqrt(float(IMPveg_sd_dec) ** 2. / (LCF_tr * float(area)))
            elif (LCF_tr != 0 and LCF_bu != 0):
                sdComb = np.sqrt(float(IMPveg_sd_dec) ** 2. / (LCF_tr * float(area)) + float(IMP_sd) ** 2. / (LCF_bu * float(area)))

            pai = LCF_bu + LCF_ev + LCF_de
            
            # paiall = (planareaB + planareaV) / AT
            porosity = 0.2  # This should change with season. Net, set for Summer
            Pv = ((-1.251 * porosity ** 2) / 1.2) + ((0.489 * porosity) / 1.2) + (0.803 / 1.2)  # factor accounting for porosity to correct total fai in roughness calc Kent et al. 2017b
            # faiall_rgh = (frontalareaB + (Pv * frontalareaV)) / (AT / (1 / scale))  # frontal area used in roughness calculation Kent et al. 2017b
            fai = Pv * (float(IMPveg_fai_eve) + float(IMPveg_fai_dec)) + float(IMP_fai)
            if (fai == 0. and pai == 1.):
                IMP_z0 = 0.
                IMP_zd = zH
            elif (fai == 0. and pai < 1.):
                IMP_z0 = 0.
                IMP_zd = 0.
            else:
                IMP_zd, IMP_z0 = rg.RoughnessCalc("Kan", zH, fai, pai, zMax, sdComb)

            # clean up and give open country values if non-existant
            if np.isnan(IMP_z0) or IMP_z0 < 0.03:
                IMP_z0 = 0.03
            if np.isnan(IMP_zd) or IMP_zd < 0.2:
                IMP_zd = 0.2

            # # new for z (2017)
            try:
                z = ((float(IMP_heights_mean) * float(LCF_buildings) + float(IMPveg_heights_mean_eve) * float(LCF_evergreen) +
                    float(IMPveg_heights_mean_dec) * float(LCF_decidious)) / (float(LCF_buildings) + float(LCF_evergreen) + float(LCF_decidious))) * 3
            except:
                z = 10.
            if z < 10.:
                z = 10.

            # Add pop density to yml
            if settings_dict['pop_density'] is not None:
                pop_density_night = feature.attribute(settings_dict['pop_density'])
            else:
                pop_density_night = -999

            if settings_dict['daypop'] == 1:
                pop_density_day = feature.attribute(settings_dict['pop_density_day'])
            else:
                pop_density_day = pop_density_night

            # # TODO Not sure on these settings OB 200825 i suggest go with default values for these
            # LUMPS_drate = 0.25
            # LUMPS_Cover = 1
            # LUMPS_MaxRes = 10
            # NARP_Trans = 1
        
            # name-description + site/landcover
            TrafficRate_WD = 0.0135 ## Already in dict
            TrafficRate_WE = 0.0095 ## Already in dict
            AnEm = fill_AnEm_yaml(db_dict, parameter_dict['AnthropogenicCode'], profiles_dict, parameter_dict, settings_dict['start_DLS'], settings_dict['end_DLS'], TrafficRate_WD, TrafficRate_WE, pop_density_night, pop_density_day, zenodo)

            # Lägg till grids i listan inom en loop
            temp_grid['properties']['lat']['value'] =  round(float(lonlat[0]), 3)
            temp_grid['properties']['lng']['value'] = round(float(lonlat[1]), 3)
            temp_grid['properties']['alt']['value'] = altitude

            temp_grid['properties']['timezone']['value'] = settings_dict['utc']
            temp_grid['properties']['surfacearea']['value'] = round(hectare , 2)
            temp_grid['properties']['z']['value'] = float('%.3f' % z)
            temp_grid['properties']['z0m_in']['value'] = float('%.3f' % IMP_z0)
            temp_grid['properties']['zdm_in']['value'] = float('%.3f' % IMP_zd)
            temp_grid['properties']['zdm_in']['value'] = float('%.3f' % IMP_zd)
            temp_grid['properties']['z0m_in']['value'] = float('%.3f' % IMP_z0)
            
            # TODO These are set according to default yaml
            # temp_grid['properties']['pipecapacity']['value'] = PipeCap
            # temp_grid['properties']['runofftowater']['value'] = RunoffToWater
            # temp_grid['properties']['narp_trans_site']['value'] = NARP_Trans
            # temp_grid['properties']['lumps'] = {
            #         'raincover': {'value': LUMPS_Cover},
            #         'rainmaxres': {'value': LUMPS_MaxRes},
            #         'drainrt': {'value': LUMPS_drate},
            #         'veg_type': {'value': 1}  # TODO WHAT?
            #         }

            # temp_grid['properties']['spartacus'] = { # At the moment, these variables are just standard. we dont know from where they come
            #             'air_ext_lw': {'value': 0.0},
            #             'air_ext_sw': {'value': 0.0},
            #             'air_ssa_lw': {'value': 0.5},
            #             'air_ssa_sw': {'value': 0.5},
            #             'ground_albedo_dir_mult_fact': {'value': 1.0},
            #             'n_stream_lw_urban': {'value': 2},
            #             'n_stream_sw_urban': {'value': 2},
            #             'n_vegetation_region_urban': {'value': 1},
            #             'sw_dn_direct_frac': {'value': 0.5},
            #             'use_sw_direct_albedo': {'value': 1.0},
            #             'veg_contact_fraction_const': {'value': 0.5},
            #             'veg_fsd_const': {'value': 0.5},
            #             'veg_ssa_lw': {'value': 0.5},
            #             'veg_ssa_sw': {'value': 0.5}
            #         },

            # TODO We dont do anyhting with STEBBS at the moment
            # temp_grid['properties']['stebbs'] = {
            #         'WallInternalConvectionCoefficient': {'value': 0.0},
            #         'InternalMassConvectionCoefficient': {'value': 0.0},
            #         'FloorInternalConvectionCoefficient': {'value': 0.0},
            #         'WindowInternalConvectionCoefficient': {'value': 0.0},
            #         'WallExternalConvectionCoefficient': {'value': 0.0},
            #         'WindowExternalConvectionCoefficient': {'value': 0.0},
            #         'GroundDepth': {'value': 0.0},
            #         'ExternalGroundConductivity': {'value': 0.0},
            #         'IndoorAirDensity': {'value': 0.0},
            #         'IndoorAirCp': {'value': 0.0},
            #         'WallBuildingViewFactor': {'value': 0.0},
            #         'WallGroundViewFactor': {'value': 0.0},
            #         'WallSkyViewFactor': {'value': 0.0},
            #         'MetabolicRate': {'value': 0.0},
            #         'LatentSensibleRatio': {'value': 0.0},
            #         'ApplianceRating': {'value': 0.0},
            #         'TotalNumberofAppliances': {'value': 0},
            #         'ApplianceUsageFactor': {'value': 0.0},
            #         'HeatingSystemEfficiency': {'value': 0.0},
            #         'MaxCoolingPower': {'value': 0.0},
            #         'CoolingSystemCOP': {'value': 0.0},
            #         'VentilationRate': {'value': 0.0},
            #         'IndoorAirStartTemperature': {'value': 0.0},
            #         'IndoorMassStartTemperature': {'value': 0.0},
            #         'WallIndoorSurfaceTemperature': {'value': 0.0},
            #         'WallOutdoorSurfaceTemperature': {'value': 0.0},
            #         'WindowIndoorSurfaceTemperature': {'value': 0.0},
            #         'WindowOutdoorSurfaceTemperature': {'value': 0.0},
            #         'GroundFloorIndoorSurfaceTemperature': {'value': 0.0},
            #         'GroundFloorOutdoorSurfaceTemperature': {'value': 0.0},
            #         'WaterTankTemperature': {'value': 0.0},
            #         'InternalWallWaterTankTemperature': {'value': 0.0},
            #         'ExternalWallWaterTankTemperature': {'value': 0.0},
            #         'WaterTankWallThickness': {'value': 0.0},
            #         'MainsWaterTemperature': {'value': 0.0},
            #         'WaterTankSurfaceArea': {'value': 0.0},
            #         'HotWaterHeatingSetpointTemperature': {'value': 0.0},
            #         'HotWaterTankWallEmissivity': {'value': 0.0},
            #         'DomesticHotWaterTemperatureInUseInBuilding': {'value': 0.0},
            #         'InternalWallDHWVesselTemperature': {'value': 0.0},
            #         'ExternalWallDHWVesselTemperature': {'value': 0.0},
            #         'DHWVesselWallThickness': {'value': 0.0},
            #         'DHWWaterVolume': {'value': 0.0},
            #         'DHWSurfaceArea': {'value': 0.0},
            #         'DHWVesselEmissivity': {'value': 0.0},
            #         'HotWaterFlowRate': {'value': 0.0},
            #         'DHWDrainFlowRate': {'value': 0.0},
            #         'DHWSpecificHeatCapacity': {'value': 0.0},
            #         'HotWaterTankSpecificHeatCapacity': {'value': 0.0},
            #         'DHWVesselSpecificHeatCapacity': {'value': 0.0},
            #         'DHWDensity': {'value': 0.0},
            #         'HotWaterTankWallDensity': {'value': 0.0},
            #         'DHWVesselDensity': {'value': 0.0},
            #         'HotWaterTankBuildingWallViewFactor': {'value': 0.0},
            #         'HotWaterTankInternalMassViewFactor': {'value': 0.0},
            #         'HotWaterTankWallConductivity': {'value': 0.0},
            #         'HotWaterTankInternalWallConvectionCoefficient': {'value': 0.0},
            #         'HotWaterTankExternalWallConvectionCoefficient': {'value': 0.0},
            #         'DHWVesselWallConductivity': {'value': 0.0},
            #         'DHWVesselInternalWallConvectionCoefficient': {'value': 0.0},
            #         'DHWVesselExternalWallConvectionCoefficient': {'value': 0.0},
            #         'DHWVesselWallEmissivity': {'value': 0.0},
            #         'HotWaterHeatingEfficiency': {'value': 0.0},
            #         'MinimumVolumeOfDHWinUse': {'value': 0.0}
            #     },

            #     'building_archetype': {
            #         'BuildingType': 'SampleType',
            #         'BuildingName': 'SampleBuilding',
            #         'BuildingCount': {'value': 1},
            #         'Occupants': {'value': 1},
            #         'stebbs_Height': {'value': 10.0},
            #         'FootprintArea': {'value': 64.0},
            #         'WallExternalArea': {'value': 80.0},
            #         'RatioInternalVolume': {'value': 0.0},
            #         'WWR': {'value': 0.2},
            #         'WallThickness': {'value': 20.0},
            #         'WallEffectiveConductivity': {'value': 60.0},
            #         'WallDensity': {'value': 1600.0},
            #         'WallCp': {'value': 850.0},
            #         'Wallx1': {'value': 1.0},
            #         'WallExternalEmissivity': {'value': 0.9},
            #         'WallInternalEmissivity': {'value': 0.9},
            #         'WallTransmissivity': {'value': 0.0},
            #         'WallAbsorbtivity': {'value': 0.8},
            #         'WallReflectivity': {'value': 0.2},
            #         'FloorThickness': {'value': 0.2},
            #         'GroundFloorEffectiveConductivity': {'value': 0.15},
            #         'GroundFloorDensity': {'value': 500.0},
            #         'GroundFloorCp': {'value': 1500.0},
            #         'WindowThickness': {'value': 0.015},
            #         'WindowEffectiveConductivity': {'value': 1.0},
            #         'WindowDensity': {'value': 2500.0},
            #         'WindowCp': {'value': 840.0},
            #         'WindowExternalEmissivity': {'value': 0.9},
            #         'WindowInternalEmissivity': {'value': 0.9},
            #         'WindowTransmissivity': {'value': 0.9},
            #         'WindowAbsorbtivity': {'value': 0.01},
            #         'WindowReflectivity': {'value': 0.09},
            #         'InternalMassDensity': {'value': 0.0},
            #         'InternalMassCp': {'value': 0.0},
            #         'InternalMassEmissivity': {'value': 0.0},
            #         'MaxHeatingPower': {'value': 0.0},
            #         'WaterTankWaterVolume': {'value': 0.0},
            #         'MaximumHotWaterHeatingPower': {'value': 0.0},
            #         'HeatingSetpointTemperature': {'value': 15.0},
            #         'CoolingSetpointTemperature': {'value': 25.0}
            #         },

            # Conductance # TODO Create function in db_functions to fill this too
            temp_grid['properties']['conductance'] = {
                    'g_max': {'value': cond_dict['G1']},
                    'g_k': {'value': cond_dict['G2']},
                    'g_q_base': {'value': cond_dict['G3']},
                    'g_q_shape': {'value': cond_dict['G4']},
                    'g_t': {'value': cond_dict['G5']},
                    'g_sm': {'value': cond_dict['G6']},
                    'kmax': {'value': cond_dict['Kmax']},
                    # 'gsmodel': {'value': cond_dict['gsModel']}, # Moved to physics 20250626
                    's1': {'value': cond_dict['S1']},
                    's2': {'value': cond_dict['S2']},
                    'tl': {'value': cond_dict['TL']},
                    'th': {'value': cond_dict['TH']},
                    'ref': {
                        'desc': db_dict['Conductance'].loc[cond_dict['Code'], 'nameOrigin'],
                        'ID': str(cond_dict['Code']),
                        'DOI':  zenodo
                         }
                    }

            temp_grid['properties']['irrigation'] = fill_irrigation_yaml(db_dict['Irrigation'].loc[parameter_dict['IrrigationCode']] , profiles_dict, zenodo)
            
            temp_grid['properties']['anthropogenic_emissions'] = AnEm
                
            temp_grid['properties']['snow'] = fill_snow_yaml(db_dict['Snow'], snow_dict, profiles_dict, zenodo)

            temp_land_cover = {
                'paved' : fill_nonveg_yaml(nonVeg_dict[feat_id], 'Paved', db_dict, LCF_paved, IrrFr_Paved, 0, 0, zenodo, temp_grid['properties']['land_cover']['paved']), # Paved has no mean height or FAI
                'bldgs' : fill_nonveg_yaml(nonVeg_dict[feat_id], 'Buildings', db_dict, LCF_buildings, IrrFr_Bldgs, IMP_fai, IMP_heights_mean, zenodo,temp_grid['properties']['land_cover']['bldgs']),
                'evetr' : fill_veg_yaml(veg_dict, db_dict, 'Evergreen Tree', LCF_evergreen, irrFr_EveTr, IMPveg_fai_eve, IMPveg_heights_mean_eve, parameter_dict['IrrigationCode'], zenodo, temp_grid['properties']['land_cover']['evetr']),
                'dectr' : fill_veg_yaml(veg_dict, db_dict, 'Deciduous Tree', LCF_decidious, irrFr_DecTr, IMPveg_fai_dec, IMPveg_heights_mean_dec,parameter_dict['IrrigationCode'], zenodo, temp_grid['properties']['land_cover']['dectr']),
                'grass' : fill_veg_yaml(veg_dict, db_dict, 'Grass', LCF_grass, irrFr_Grass, 0, 0, parameter_dict['IrrigationCode'], zenodo, temp_grid['properties']['land_cover']['grass']), # grass has no mean height or FAI
                'bsoil' : fill_bare_soil_yaml(nonVeg_dict[feat_id]['Bare Soil'], db_dict, LCF_baresoil, IrrFr_BSoil, zenodo, temp_grid['properties']['land_cover']['bsoil']) ,
                'water' : fill_water_yaml(water_dict, db_dict, LCF_water, IrrFr_Water, zenodo,temp_grid['properties']['land_cover']['water'])
            }

            temp_grid['properties']['land_cover'] = temp_land_cover
            
            if settings_dict['spartacus'] == 1:  
                temp_grid['properties']['vertical_layers'] = gridlayoutOut[feat_id]['vertical_layers'][0]
                temp_grid['properties']['lambda_c']['value'] = round(float((gridlayoutOut[feat_id]['wallAreaGrid']) / (hectare * LCF_buildings)) + 1, 4)
            
            temp_grid['properties']['n_buildings']['value'] = gridlayoutOut[feat_id]['buildings_count'] # buildings_count_dict[feat_id]
            temp_grid['properties']['h_std']['value'] = IMP_sd

            # TODO we dont set any initial states except for seasonal cycles and for roof/wall
            # evetree
            temp_grid['initial_states']['evetr']['alb_id'] = {'value': leaf_cycle_dict[leaf_cycle]['albEveTr0']}
            temp_grid['initial_states']['evetr']['lai_id'] = {'value': leaf_cycle_dict[leaf_cycle]['laiinitialevetr']}
            temp_grid['initial_states']['evetr']['gdd_id'] = {'value': leaf_cycle_dict[leaf_cycle]['gdd_1_0']}
            temp_grid['initial_states']['evetr']['sdd_id'] = {'value': leaf_cycle_dict[leaf_cycle]['gdd_2_0']}                
            # dectr
            temp_grid['initial_states']['dectr']['alb_id'] = {'value': leaf_cycle_dict[leaf_cycle]['albDecTr0']}
            temp_grid['initial_states']['dectr']['lai_id'] = {'value': leaf_cycle_dict[leaf_cycle]['laiinitialdectr']}
            temp_grid['initial_states']['dectr']['gdd_id'] = {'value': leaf_cycle_dict[leaf_cycle]['gdd_1_0']}
            temp_grid['initial_states']['dectr']['sdd_id'] = {'value': leaf_cycle_dict[leaf_cycle]['gdd_2_0']}                
            temp_grid['initial_states']['dectr']['porosity_id'] = {'value': leaf_cycle_dict[leaf_cycle]['porosity0']}
            temp_grid['initial_states']['dectr']['decidcap_id'] = {'value': leaf_cycle_dict[leaf_cycle]['decidCap0']}
            # grass
            temp_grid['initial_states']['grass']['alb_id'] = {'value': leaf_cycle_dict[leaf_cycle]['albGrass0']}
            temp_grid['initial_states']['grass']['lai_id'] = {'value': leaf_cycle_dict[leaf_cycle]['laiinitialgrass']}
            temp_grid['initial_states']['grass']['gdd_id'] = {'value': leaf_cycle_dict[leaf_cycle]['gdd_1_0']}
            temp_grid['initial_states']['grass']['sdd_id'] = {'value': leaf_cycle_dict[leaf_cycle]['gdd_2_0']}        

            # initial_states = {
            #     'snowalb': {'value': 0.3},

            #     'paved': {
            #     'state': {'value': 0.0},
            #     'soilstore': {'value': 10},
            #     'snowfrac': {'value': 0.0},
            #     'snowpack': {'value': 0.0},
            #     'icefrac': {'value': 0.0},
            #     'snowwater': {'value': 0.0},
            #     'snowdens': {'value': 0.0},
            #     'temperature': {'value': [15.0, 15.0, 15.0, 15.0, 15.0]},
            #     'tsfc': {'value': 15.0},
            #     'tin': {'value': 20.0}
            #     },

            #     'bldgs': {
            #     'state': {'value': 0.0},
            #     'soilstore': {'value': 10},
            #     'snowfrac': {'value': 0.0},
            #     'snowpack': {'value': 0.0},
            #     'icefrac': {'value': 0.0},
            #     'snowwater': {'value': 0.0},
            #     'snowdens': {'value': 0.0},
            #     'temperature': {'value': [15.0, 15.0, 15.0, 15.0, 15.0]},
            #     'tsfc': {'value': 15.0},
            #     'tin': {'value': 20.0}
            #     },

            #     'evetr': {
            #     'state': {'value': 0.0},
            #     'soilstore': {'value': 10},
            #     'snowfrac': {'value': 0.0},
            #     'snowpack': {'value': 0.0},
            #     'icefrac': {'value': 0.0},
            #     'snowwater': {'value': 0.0},
            #     'snowdens': {'value': 0.0},
            #     'temperature': {'value': [15.0, 15.0, 15.0, 15.0, 15.0]},
            #     'tsfc': {'value': 15.0},
            #     'tin': {'value': 20.0},
            #     'alb_id': {'value': leaf_cycle_dict[leaf_cycle]['albEveTr0']},
            #     'lai_id': {'value': leaf_cycle_dict[leaf_cycle]['laiinitialevetr']},
            #     'gdd_id': {'value': leaf_cycle_dict[leaf_cycle]['gdd_1_0']},
            #     'sdd_id': {'value': leaf_cycle_dict[leaf_cycle]['gdd_2_0']},
            #     'wu': {
            #         'wu_total': {'value': 0.0},
            #         'wu_auto': {'value': 0.0},
            #         'wu_manual': {'value': 0.0}
            #     }
            #     },

            #     'dectr': {
            #     'state': {'value': 0.0},
            #     'soilstore': {'value': 10},
            #     'snowfrac': {'value': 0.0},
            #     'snowpack': {'value': 0.0},
            #     'icefrac': {'value': 0.0},
            #     'snowwater': {'value': 0.0},
            #     'snowdens': {'value': 0.0},
            #     'temperature': {'value': [15.0, 15.0, 15.0, 15.0, 15.0]},
            #     'tsfc': {'value': 15.0},
            #     'tin': {'value': 20.0},
            #     'alb_id': {'value': leaf_cycle_dict[leaf_cycle]['albDecTr0']},
            #     'lai_id': {'value': leaf_cycle_dict[leaf_cycle]['laiinitialdectr']},
            #     'gdd_id': {'value': leaf_cycle_dict[leaf_cycle]['gdd_1_0']},
            #     'sdd_id': {'value': leaf_cycle_dict[leaf_cycle]['gdd_2_0']},
            #     'wu': {
            #         'wu_total': {'value': 0.0},
            #         'wu_auto': {'value': 0.0},
            #         'wu_manual': {'value': 0.0}
            #     },
            #     'porosity_id': {'value': leaf_cycle_dict[leaf_cycle]['porosity0']},
            #     'decidcap_id': {'value': leaf_cycle_dict[leaf_cycle]['decidCap0']}
            #     },

            #     'grass': {
            #     'state': {'value': 0.0},
            #     'soilstore': {'value': 10},
            #     'snowfrac': {'value': 0.0},
            #     'snowpack': {'value': 0.0},
            #     'icefrac': {'value': 0.0},
            #     'snowwater': {'value': 0.0},
            #     'snowdens': {'value': 0.0},
            #     'temperature': {'value': [15.0, 15.0, 15.0, 15.0, 15.0]},
            #     'tsfc': {'value': 15.0},
            #     'tin': {'value': 20.0},
            #     'alb_id': {'value': leaf_cycle_dict[leaf_cycle]['albGrass0']},
            #     'lai_id': {'value': leaf_cycle_dict[leaf_cycle]['laiinitialgrass']},
            #     'gdd_id': {'value': leaf_cycle_dict[leaf_cycle]['gdd_1_0']},
            #     'sdd_id': {'value': leaf_cycle_dict[leaf_cycle]['gdd_2_0']},
            #     'wu': {
            #         'wu_total': {'value': 0.0},
            #         'wu_auto': {'value': 0.0},
            #         'wu_manual': {'value': 0.0}
            #     },
            #     },

            #     'bsoil': {
            #     'state': {'value': 0.0},
            #     'soilstore': {'value': 10},
            #     'snowfrac': {'value': 0.0},
            #     'snowpack': {'value': 0.0},
            #     'icefrac': {'value': 0.0},
            #     'snowwater': {'value': 0.0},
            #     'snowdens': {'value': 0.0},
            #     'temperature': {'value': [15.0, 15.0, 15.0, 15.0, 15.0]},
            #     'tsfc': {'value': 15.0},
            #     'tin': {'value': 20.0}
            #     },

            #     'water': {
            #     'state': {'value': 20000.0},
            #     'soilstore': {'value': 10},
            #     'snowfrac': {'value': 0.0},
            #     'snowpack': {'value': 0.0},
            #     'icefrac': {'value': 0.0},
            #     'snowwater': {'value': 0.0},
            #     'snowdens': {'value': 0.0},
            #     'temperature': {'value': [15.0, 15.0, 15.0, 15.0, 15.0]},
            #     'tsfc': {'value': 15.0},
            #     'tin': {'value': 20.0}
            #     },

            #     'roofs': [{
            #         'state': {'value': 0.0},
            #         'soilstore': {'value': 10},
            #         'snowfrac': {'value': 0.0},
            #         'snowpack': {'value': 0.0},
            #         'icefrac': {'value': 0.0},
            #         'snowwater': {'value': 0.0},
            #         'snowdens': {'value': 0.0},
            #         'temperature': {'value': [15.0, 15.0, 15.0, 15.0, 15.0]},
            #         'tsfc': {'value': 15.0},
            #         'tin': {'value': 20.0}
            #         }] * gridlayoutOut[feat_id]['nlayer'],
            #     'walls': [{
            #         'state': {'value': 0.0},
            #         'soilstore': {'value': 10},
            #         'snowfrac': {'value': 0.0},
            #         'snowpack': {'value': 0.0},
            #         'icefrac': {'value': 0.0},
            #         'snowwater': {'value': 0.0},
            #         'snowdens': {'value': 0.0},
            #         'temperature': {'value': [15.0, 15.0, 15.0, 15.0, 15.0]},
            #         'tsfc': {'value': 15.0},
            #         'tin': {'value': 20.0}
            #         }] * gridlayoutOut[feat_id]['nlayer']
            # }
            if settings_dict['spartacus'] == 1: # Check fraction consistency between land cover data and spartcus fractions
                temp_grid = check_fraction_consistency(temp_grid)
            
            yaml_dict['sites'].append(temp_grid)
            del temp_grid

        # Convert values in the nested dictionary
        yaml_dict_native = convert_numpy_types(yaml_dict)

        # Save dictionary to .yml
        with open(save_txt_folder + settings_dict['file_code'] + '.yml', 'w') as file:
            yaml.dump(yaml_dict_native, file, sort_keys = False)

        # SUEWSConfig(**ss_dict_native)

        # # If Spartacus dict is not empty, give user a warning. 
        if  spartacus_error_dict:
            error_string = ''
            for keys in list(spartacus_error_dict.keys()):
                error_string = error_string  + f'\n\n Error in Grid: {str(keys)} : {spartacus_error_dict[keys]}'
            
            error_string = error_string + '\n\nCheck Python console to find this information again'

            iface.messageBar().pushMessage("Warnings related to vertical morphology", error_string, level=Qgis.MessageLevel.Warning)
        
        # Pydantic check
        schema = generate_json_schema()
        is_valid, errors = validate_single_file(save_txt_folder + settings_dict['file_code'] + '.yml', schema, show_details=True)
        if not is_valid:
            pydanticError = ''
            for error in errors:
                pydanticError = pydanticError + error
            QMessageBox.information(None, 'Process Complete with warnings', "See information in the Message bar or QGIS Message log")
            iface.messageBar().pushMessage("Process completed with warnings from pydantic testing", pydanticError, level=Qgis.MessageLevel.Warning)
        else:
            iface.messageBar().pushMessage("Process completed without warnings", level=Qgis.MessageLevel.Info)
            QMessageBox.information(None, 'Process Complete', "Input files for SUEWS generated")
        

        self.dlg.progressBar.setValue(0)
    


    # def write_to_init(self, initfilein, initfileout):
    #     LeafCycle = self.leaf_cycle
    #     SoilMoisture = self.soil_moisture
    #     moist = int(SoilMoisture * 1.5)
    #     snowinitially = 0

    #     nml = f90nml.read(initfilein)

    #     nml['initialconditions']['soilstorepavedstate'] = moist
    #     nml['initialconditions']['soilstorebldgsstate'] = moist
    #     nml['initialconditions']['soilstoreevetrstate'] = moist
    #     nml['initialconditions']['soilstoredectrstate'] = moist
    #     nml['initialconditions']['soilstoregrassstate'] = moist
    #     nml['initialconditions']['soilstorebsoilstate'] = moist

    #     # Based on London data
    #     if LeafCycle == 1:  # Winter
    #         nml['initialconditions']['gdd_1_0'] = 0
    #         nml['initialconditions']['gdd_2_0'] = -450
    #         nml['initialconditions']['laiinitialevetr'] = 4
    #         nml['initialconditions']['laiinitialdectr'] = 1
    #         nml['initialconditions']['laiinitialgrass'] = 1.6
    #         nml['initialconditions']['albEveTr0'] = 0.10
    #         nml['initialconditions']['albDecTr0'] = 0.12
    #         nml['initialconditions']['albGrass0'] = 0.18
    #         nml['initialconditions']['decidCap0'] = 0.3
    #         nml['initialconditions']['porosity0'] = 0.2
    #     elif LeafCycle == 2:
    #         nml['initialconditions']['gdd_1_0'] = 50
    #         nml['initialconditions']['gdd_2_0'] = -400
    #         nml['initialconditions']['laiinitialevetr'] = 4.2
    #         nml['initialconditions']['laiinitialdectr'] = 2.0
    #         nml['initialconditions']['laiinitialgrass'] = 2.6
    #         nml['initialconditions']['albEveTr0'] = 0.10
    #         nml['initialconditions']['albDecTr0'] = 0.12
    #         nml['initialconditions']['albGrass0'] = 0.18
    #         nml['initialconditions']['decidCap0'] = 0.4
    #         nml['initialconditions']['porosity0'] = 0.3
    #     elif LeafCycle == 3:
    #         nml['initialconditions']['gdd_1_0'] = 150
    #         nml['initialconditions']['gdd_2_0'] = -300
    #         nml['initialconditions']['laiinitialevetr'] = 4.6
    #         nml['initialconditions']['laiinitialdectr'] = 3.0
    #         nml['initialconditions']['laiinitialgrass'] = 3.6
    #         nml['initialconditions']['albEveTr0'] = 0.10
    #         nml['initialconditions']['albDecTr0'] = 0.12
    #         nml['initialconditions']['albGrass0'] = 0.18
    #         nml['initialconditions']['decidCap0'] = 0.6
    #         nml['initialconditions']['porosity0'] = 0.5
    #     elif LeafCycle == 4:
    #         nml['initialconditions']['gdd_1_0'] = 225
    #         nml['initialconditions']['gdd_2_0'] = -150
    #         nml['initialconditions']['laiinitialevetr'] = 4.9
    #         nml['initialconditions']['laiinitialdectr'] = 4.5
    #         nml['initialconditions']['laiinitialgrass'] = 4.6
    #         nml['initialconditions']['albEveTr0'] = 0.10
    #         nml['initialconditions']['albDecTr0'] = 0.12
    #         nml['initialconditions']['albGrass0'] = 0.18
    #         nml['initialconditions']['decidCap0'] = 0.8
    #         nml['initialconditions']['porosity0'] = 0.6
    #     elif LeafCycle == 5:  # Summer
    #         nml['initialconditions']['gdd_1_0'] = 300
    #         nml['initialconditions']['gdd_2_0'] = 0
    #         nml['initialconditions']['laiinitialevetr'] = 5.1
    #         nml['initialconditions']['laiinitialdectr'] = 5.5
    #         nml['initialconditions']['laiinitialgrass'] = 5.9
    #         nml['initialconditions']['albEveTr0'] = 0.10
    #         nml['initialconditions']['albDecTr0'] = 0.12
    #         nml['initialconditions']['albGrass0'] = 0.18
    #         nml['initialconditions']['decidCap0'] = 0.8
    #         nml['initialconditions']['porosity0'] = 0.6
    #     elif LeafCycle == 6:
    #         nml['initialconditions']['gdd_1_0'] = 225
    #         nml['initialconditions']['gdd_2_0'] = -150
    #         nml['initialconditions']['laiinitialevetr'] = 4.9
    #         nml['initialconditions']['laiinitialdectr'] = 4, 5
    #         nml['initialconditions']['laiinitialgrass'] = 4.6
    #         nml['initialconditions']['albEveTr0'] = 0.10
    #         nml['initialconditions']['albDecTr0'] = 0.12
    #         nml['initialconditions']['albGrass0'] = 0.18
    #         nml['initialconditions']['decidCap0'] = 0.8
    #         nml['initialconditions']['porosity0'] = 0.5
    #     elif LeafCycle == 7:
    #         nml['initialconditions']['gdd_1_0'] = 150
    #         nml['initialconditions']['gdd_2_0'] = -300
    #         nml['initialconditions']['laiinitialevetr'] = 4.6
    #         nml['initialconditions']['laiinitialdectr'] = 3.0
    #         nml['initialconditions']['laiinitialgrass'] = 3.6
    #         nml['initialconditions']['albEveTr0'] = 0.10
    #         nml['initialconditions']['albDecTr0'] = 0.12
    #         nml['initialconditions']['albGrass0'] = 0.18
    #         nml['initialconditions']['decidCap0'] = 0.5
    #         nml['initialconditions']['porosity0'] = 0.4
    #     elif LeafCycle == 8:  # Late Autumn
    #         nml['initialconditions']['gdd_1_0'] = 50
    #         nml['initialconditions']['gdd_2_0'] = -400
    #         nml['initialconditions']['laiinitialevetr'] = 4.2
    #         nml['initialconditions']['laiinitialdectr'] = 2.0
    #         nml['initialconditions']['laiinitialgrass'] = 2.6
    #         nml['initialconditions']['albEveTr0'] = 0.10
    #         nml['initialconditions']['albDecTr0'] = 0.12
    #         nml['initialconditions']['albGrass0'] = 0.18
    #         nml['initialconditions']['decidCap0'] = 0.4
    #         nml['initialconditions']['porosity0'] = 0.2

    #     nml['initialconditions']['snowinitially'] = snowinitially

    #     nml.write(initfileout, force=True)



## unused (old) functions

# def progress_update(self):
    #     self.steps += 1
    #     self.dlg.progressBar.setValue(self.steps)


# def hide_show_LCF(self, widget):
    #     if widget.LCF_checkBox.isChecked():
    #         self.LCF_from_file = False
    #         widget.LCF_Frame.show()
    #         widget.pushButtonImportLCF.hide()
    #         widget.textInputLCFData.hide()
    #     else:
    #         self.LCF_from_file = True
    #         widget.LCF_Frame.hide()
    #         widget.pushButtonImportLCF.show()
    #         widget.textInputLCFData.show()

    # def hide_show_IMP(self, widget):
    #     if widget.IMP_checkBox.isChecked():
    #         self.IMP_from_file = False
    #         widget.IMP_Frame.show()
    #         widget.pushButtonImportIMPBuild.hide()
    #         widget.textInputIMPData.hide()
    #     else:
    #         self.IMP_from_file = True
    #         widget.IMP_Frame.hide()
    #         widget.pushButtonImportIMPBuild.show()
    #         widget.textInputIMPData.show()

    # def hide_show_IMPveg(self, widget):
    #     if widget.IMPveg_checkBox.isChecked():
    #         self.IMPveg_from_file = False
    #         widget.IMPveg_Frame.show()
    #         widget.pushButtonImportIMPVeg.hide()
    #         widget.textInputIMPVegData.hide()
    #         widget.checkBox_twovegfiles.hide()
    #         widget.pushButtonImportIMPVeg_eve.hide()
    #         widget.pushButtonImportIMPVeg_dec.hide()
    #         widget.textInputIMPEveData.hide()
    #         widget.textInputIMPDecData.hide()
    #     else:
    #         self.IMPveg_from_file = True
    #         widget.IMPveg_Frame.hide()
    #         widget.pushButtonImportIMPVeg.show()
    #         widget.textInputIMPVegData.show()
    #         widget.checkBox_twovegfiles.show()
    #         widget.pushButtonImportIMPVeg_eve.show()
    #         widget.pushButtonImportIMPVeg_dec.show()
    #         widget.textInputIMPEveData.show()
    #         widget.textInputIMPDecData.show()

    # def grid_layer_changed(self, widget, timezone):

    #     # Try to avoid error when no gridlayer present, or layers added or removed to project
    #     try:
    #         poly = self.layerComboManagerPolygrid.currentLayer()
    #         grid_path = poly.source()
    #         utc = get_utc(grid_path, timezone)
    #         widget.lineEditUTC.setText(str(utc))
    #     except:
    #         pass


