Source code for agentlib.modules.utils.try_sensor

"""This module contains a SensorModule which reads a .dat file
from the Deutsche Wetterdienst (DWD)."""

import io
from typing import Union, List
from pydantic import FilePath, Field, validator
import numpy as np
import pandas as pd
from agentlib.core import (
    BaseModule,
    Agent,
    AgentVariable,
    BaseModuleConfig,
    AgentVariables,
)


[docs]class TRYSensorConfig(BaseModuleConfig): """Define parameters for the TRYSensor""" outputs: AgentVariables = [ AgentVariable( name="T_oda", unit="K", description="Air temperature 2m over ground [K]" ), AgentVariable( name="pressure", unit="hPa", description="Air pressure in standard height [hPa]", ), AgentVariable( name="wind_direction", unit="°", description="Wind direction 10 m above gorund " "[Grad] {0..360;999}", ), AgentVariable( name="wind_speed", unit="m/s", description="Wind speed 10 m above ground [m/s]", ), AgentVariable(name="coverage", unit="eighth", description="[eighth] {0..8;9}"), AgentVariable(name="absolute_humidity", unit="g/kg", description="[g/kg]"), AgentVariable( name="relative_humidity", unit="%", description="Relative humidity 2 m above ground " "[%] {1..100}", ), AgentVariable( name="beam_direct", unit="W/m^2", description="Direct beam of sun (hor. plane) " "[W/m^2] downwards: positive", ), AgentVariable( name="beam_diffuse", unit="/m^2", description="Diffuse beam of sun (hor. plane) " "[W/m^2] downwards: positive", ), AgentVariable( name="beam_atm", unit="/m^2", description="Beam of atmospheric heat (hor. plane) " "[W/m^2] downwards: positive", ), AgentVariable( name="beam_terr", unit="/m^2", description="Beam of terrestrial heat " "[W/m^2] upwards: negative", ), ] filename: FilePath = Field( title="filename", description="The filepath to the data." ) t_sample: Union[float, int] = Field( title="t_sample", default=1, description="Sample time of sensor. As TRY are hourly, " "default is 1 h", ) shared_variable_fields: List[str] = ["outputs"]
[docs]class TRYSensor(BaseModule): """A module that emulates a sensor using the TRY data of the DWD. The module casts the following AgentVariables into the data_broker: - T_oda: Air temperature 2m over ground [K] - pressure: Air pressure in standard height [hPa] - wind_direction: Wind direction 10 m above gorund [Grad] {0..360;999} - wind_speed: Wind speed 10 m above ground [m/s] - coverage: [eighth] {0..8;9} - absolute_humidity: [g/kg] - relative_humidity: Relative humidity 2 m above ground [%] {1..100} - beam_direct: Direct beam of sun (hor. plane) [W/m^2] downwards: positive - beam_diffuse: Diffuse beam of sun (hor. plane) [W/m^2] downwards: positive - beam_atm: Beam of atmospheric heat (hor. plane) [W/m^2] downwards: positive - beam_terr: Beam of terrestrial heat [W/m^2] upwards: negative """ config: TRYSensorConfig def __init__(self, *, config: dict, agent: Agent): """Overwrite init to enable a custom default filename which uses the agent_id.""" super().__init__(config=config, agent=agent) self.one_year = 86400 * 365 self._data = read_dat_file(self.filename) # Resample and interpolate once according to t_sample: self._data = self._data.reindex(np.arange(0, self.one_year, self.t_sample)) self._data = self._data.interpolate() # Check if outputs match the _data: _names = [v.name for v in self.variables] if set(self._data.columns).difference(_names): raise KeyError( "The internal variables differ from the " "supported data in a TRY dataset." ) @property def filename(self): """Return the filename.""" return self.config.filename @property def t_sample(self): """Return the sample rate.""" return self.config.t_sample
[docs] def process(self): """Write the current TRY values into data_broker every other t_sample""" while True: data = self.get_data_now() for key, val in data.items(): self.set(name=key, value=val) yield self.env.timeout(self.t_sample)
[docs] def get_data_now(self): """Get the data at the current env time. Reiterate the year if necessary. """ now = self.env.now % self.one_year if now in self._data.index: return self._data.loc[now] # Interpolate: df = self._data.copy() df.loc[now] = np.nan return df.sort_index().interpolate().loc[now]
[docs] def register_callbacks(self): """Dont do anything as this BaseModule is not event-triggered"""
[docs]def read_dat_file(dat_file): """ Read a .dat file from the DWD's TRY data from the given .dat-file Args: dat_file (str): The file to read Returns: """ # pylint: disable=raise-missing-from _sep = r"\s+" with open(dat_file, "r") as file: # First try for 2012 dataset: try: data_lines = file.readlines()[32:] data_lines.remove("*** \n") # Remove line "*** " except ValueError: # Now for 2007 dataset: try: data_lines = file.readlines()[36:] data_lines.remove("***\n") # Remove line "***" except ValueError: raise TypeError("Given .dat file could not be loaded.") output = io.StringIO() output.writelines(data_lines) output.seek(0) df = pd.read_csv(output, sep=_sep, header=0) # Convert the ours to seconds df.index *= 3600 _key_map = { "t": "T_oda", "p": "pressure", "WR": "wind_direction", "WG": "wind_speed", "N": "coverage", "x": "absolute_humidity", "RF": "relative_humidity", "B": "beam_direct", "D": "beam_diffuse", "A:": "beam_atm", "E": "beam_terr", } # Rename df for easier later usage. df.rename(columns=_key_map, inplace=True) df = df[df.columns.intersection(_key_map.values())] df["T_oda"] += 273.15 # To Kelvin return df