def simulate(self):
        """ Section 7 - uwg main section

            self.N                  # Total hours in simulation
            self.ph                 # per hour
            self.dayType            # 3=Sun, 2=Sat, 1=Weekday
            self.ceil_time_step     # simulation timestep (dt) fitted to weather file timestep

            # Output of object instance vector
            self.WeatherData        # Nx1 vector of forc instance
            self.UCMData            # Nx1 vector of UCM instance
            self.UBLData            # Nx1 vector of UBL instance
            self.RSMData            # Nx1 vector of RSM instance
            self.USMData            # Nx1 vector of USM instance
        """

        self.N = int(self.simTime.days * 24)       # total number of hours in simulation
        n = 0                                      # weather time step counter
        self.ph = self.simTime.dt/3600.            # dt (simulation time step) in hours

        # Data dump variables
        time = range(self.N)

        self.WeatherData = [None for x in range(self.N)]
        self.UCMData = [None for x in range(self.N)]
        self.UBLData = [None for x in range(self.N)]
        self.RSMData = [None for x in range(self.N)]
        self.USMData = [None for x in range(self.N)]

        print('\nSimulating new temperature and humidity values for {} days from {}/{}.\n'.format(
            int(self.nDay), int(self.Month), int(self.Day)))
        self.logger.info("Start simulation")

        for it in range(1, self.simTime.nt, 1):  # for every simulation time-step (i.e 5 min) defined by uwg
            # Update water temperature (estimated)
            if self.nSoil < 3: # correction to original matlab code
                # for BUBBLE/CAPITOUL/Singapore only
                self.forc.deepTemp = sum(self.forcIP.temp)/float(len(self.forcIP.temp))
                self.forc.waterTemp = sum(
                    self.forcIP.temp)/float(len(self.forcIP.temp)) - 10.      # for BUBBLE/CAPITOUL/Singapore only
            else:
                # soil temperature by depth, by month
                self.forc.deepTemp = self.Tsoil[self.soilindex1][self.simTime.month-1]
                self.forc.waterTemp = self.Tsoil[2][self.simTime.month-1]

            # There's probably a better way to update the weather...
            self.simTime.UpdateDate()

            self.logger.info("\n{0} m={1}, d={2}, h={3}, s={4}".format(
                __name__, self.simTime.month, self.simTime.day, self.simTime.secDay/3600., self.simTime.secDay))

            # simulation time increment raised to weather time step
            self.ceil_time_step = int(math.ceil(it * self.ph))-1
            # minus one to be consistent with forcIP list index
            # Updating forcing instance
            # horizontal Infrared Radiation Intensity (W m-2)
            self.forc.infra = self.forcIP.infra[self.ceil_time_step]
            # wind speed (m s-1)
            self.forc.wind = max(self.forcIP.wind[self.ceil_time_step], self.geoParam.windMin)
            self.forc.uDir = self.forcIP.uDir[self.ceil_time_step]          # wind direction
            # specific humidty (kg kg-1)
            self.forc.hum = self.forcIP.hum[self.ceil_time_step]
            self.forc.pres = self.forcIP.pres[self.ceil_time_step]          # Pressure (Pa)
            self.forc.temp = self.forcIP.temp[self.ceil_time_step]          # air temperature (C)
            self.forc.rHum = self.forcIP.rHum[self.ceil_time_step]          # Relative humidity (%)
            self.forc.prec = self.forcIP.prec[self.ceil_time_step]          # Precipitation (mm h-1)
            # horizontal solar diffuse radiation (W m-2)
            self.forc.dif = self.forcIP.dif[self.ceil_time_step]
            # normal solar direct radiation (W m-2)
            self.forc.dir = self.forcIP.dir[self.ceil_time_step]
            # Canyon humidity (absolute) same as rural
            self.UCM.canHum = copy.copy(self.forc.hum)

            # Update solar flux
            self.solar = SolarCalcs(self.UCM, self.BEM, self.simTime,
                                    self.RSM, self.forc, self.geoParam, self.rural)
            self.rural, self.UCM, self.BEM = self.solar.solarcalcs()

            # Update building & traffic schedule
            # Assign day type (1 = weekday, 2 = sat, 3 = sun/other)
            if self.is_near_zero(self.simTime.julian % 7):
                self.dayType = 3                                        # Sunday
            elif self.is_near_zero(self.simTime.julian % 7 - 6.):
                self.dayType = 2                                        # Saturday
            else:
                self.dayType = 1                                        # Weekday

            # Update anthropogenic heat load for each hour (building & UCM)
            self.UCM.sensAnthrop = self.sensAnth * (self.SchTraffic[self.dayType-1][self.simTime.hourDay])

            # Update the energy components for building types defined in initialize.uwg
            for i in range(len(self.BEM)):
                # Set temperature
                self.BEM[i].building.coolSetpointDay = self.Sch[i].Cool[self.dayType -
                                                                        1][self.simTime.hourDay] + 273.15  # add from temperature schedule for cooling
                self.BEM[i].building.coolSetpointNight = self.BEM[i].building.coolSetpointDay
                self.BEM[i].building.heatSetpointDay = self.Sch[i].Heat[self.dayType -
                                                                        1][self.simTime.hourDay] + 273.15  # add from temperature schedule for heating
                self.BEM[i].building.heatSetpointNight = self.BEM[i].building.heatSetpointDay

                # Internal Heat Load Schedule (W/m^2 of floor area for Q)
                self.BEM[i].Elec = self.Sch[i].Qelec * self.Sch[i].Elec[self.dayType -
                                                                        1][self.simTime.hourDay]      # Qelec x elec fraction for day
                self.BEM[i].Light = self.Sch[i].Qlight * self.Sch[i].Light[self.dayType -
                                                                           1][self.simTime.hourDay]    # Qlight x light fraction for day
                self.BEM[i].Nocc = self.Sch[i].Nocc * self.Sch[i].Occ[self.dayType -
                                                                      1][self.simTime.hourDay]        # Number of occupants x occ fraction for day
                # Sensible Q occupant * fraction occupant sensible Q * number of occupants
                self.BEM[i].Qocc = self.sensOcc * (1 - self.LatFOcc) * self.BEM[i].Nocc

                # SWH and ventilation schedule
                self.BEM[i].SWH = self.Sch[i].Vswh * self.Sch[i].SWH[self.dayType -
                                                                     1][self.simTime.hourDay]          # litres per hour x SWH fraction for day
                # m^3/s/m^2 of floor
                self.BEM[i].building.vent = self.Sch[i].Vent
                self.BEM[i].Gas = self.Sch[i].Qgas * self.Sch[i].Gas[self.dayType -
                                                                     1][self.simTime.hourDay]          # Gas Equip Schedule, per m^2 of floor

                # This is quite messy, should update
                # Update internal heat and corresponding fractional loads
                intHeat = self.BEM[i].Light + self.BEM[i].Elec + self.BEM[i].Qocc
                # W/m2 from light, electricity, occupants
                self.BEM[i].building.intHeatDay = intHeat
                self.BEM[i].building.intHeatNight = intHeat
                # fraction of radiant heat from light and equipment of whole internal heat
                self.BEM[i].building.intHeatFRad = (
                    self.RadFLight * self.BEM[i].Light + self.RadFEquip * self.BEM[i].Elec) / intHeat
                # fraction of latent heat (from occupants) of whole internal heat
                self.BEM[i].building.intHeatFLat = self.LatFOcc * \
                    self.sensOcc * self.BEM[i].Nocc/intHeat

                # Update envelope temperature layers
                self.BEM[i].T_wallex = self.BEM[i].wall.layerTemp[0]
                self.BEM[i].T_wallin = self.BEM[i].wall.layerTemp[-1]
                self.BEM[i].T_roofex = self.BEM[i].roof.layerTemp[0]
                self.BEM[i].T_roofin = self.BEM[i].roof.layerTemp[-1]

            # Update rural heat fluxes & update vertical diffusion model (VDM)
            self.rural.infra = self.forc.infra - self.rural.emissivity * self.SIGMA * \
                self.rural.layerTemp[0]**4.    # Infrared radiation from rural road

            self.rural.SurfFlux(self.forc, self.geoParam, self.simTime,
                                self.forc.hum, self.forc.temp, self.forc.wind, 2., 0.)
            self.RSM.VDM(self.forc, self.rural, self.geoParam, self.simTime)

            # Calculate urban heat fluxes, update UCM & UBL
            self.UCM, self.UBL, self.BEM = urbflux(
                self.UCM, self.UBL, self.BEM, self.forc, self.geoParam, self.simTime, self.RSM)
            self.UCM.UCModel(self.BEM, self.UBL.ublTemp, self.forc, self.geoParam)
            self.UBL.UBLModel(self.UCM, self.RSM, self.rural,
                              self.forc, self.geoParam, self.simTime)

            """
            # Experimental code to run diffusion model in the urban area
            # N.B Commented out in python uwg because computed wind speed in
            # urban VDM: y = =0.84*ln((2-x/20)/0.51) results in negative log
            # for building heights >= 40m.

            Uroad = copy.copy(self.UCM.road)
            Uroad.sens = copy.copy(self.UCM.sensHeat)
            Uforc = copy.copy(self.forc)
            Uforc.wind = copy.copy(self.UCM.canWind)
            Uforc.temp = copy.copy(self.UCM.canTemp)
            self.USM.VDM(Uforc,Uroad,self.geoParam,self.simTime)
            """

            self.logger.info("dbT = {}".format(self.UCM.canTemp-273.15))
            if n > 0:
                logging.info("dpT = {}".format(self.UCM.Tdp))
                logging.info("RH  = {}".format(self.UCM.canRHum))

            if self.is_near_zero(self.simTime.secDay % self.simTime.timePrint) and n < self.N:

                self.logger.info("{0} ----sim time step = {1}----\n\n".format(__name__, n))

                self.WeatherData[n] = copy.copy(self.forc)
                _Tdb, _w, self.UCM.canRHum, _h, self.UCM.Tdp, _v = psychrometrics(
                    self.UCM.canTemp, self.UCM.canHum, self.forc.pres)

                self.UBLData[n] = copy.copy(self.UBL)
                self.UCMData[n] = copy.copy(self.UCM)
                self.RSMData[n] = copy.copy(self.RSM)

                self.logger.info("dbT = {}".format(self.UCMData[n].canTemp-273.15))
                self.logger.info("dpT = {}".format(self.UCMData[n].Tdp))
                self.logger.info("RH  = {}".format(self.UCMData[n].canRHum))

                n += 1