Skip to content

Commit

Permalink
Merge pull request #1798 from gridsingularity/feature/GSYE-766
Browse files Browse the repository at this point in the history
Feature/gsye 766
  • Loading branch information
hannesdiedrich authored Oct 2, 2024
2 parents 6323ba3 + f442047 commit 768ecfd
Show file tree
Hide file tree
Showing 8 changed files with 419 additions and 170 deletions.
6 changes: 6 additions & 0 deletions src/gsy_e/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""

# Need to import required settings from gsy-framework in order to be available in d3a,
# thus avoiding accessing the gsy-framework constants.
# pylint: disable=unused-import
Expand Down Expand Up @@ -66,14 +67,19 @@
CONNECT_TO_PROFILES_DB = False
SEND_EVENTS_RESPONSES_TO_SDK_VIA_RQ = False

RUN_IN_NON_P2P_MODE = False

DEFAULT_SCM_COMMUNITY_NAME = "Community"
DEFAULT_SCM_GRID_NAME = "Grid"

FORWARD_MARKET_MAX_DURATION_YEARS = 6

MIN_OFFER_BID_AGE_P2P_DISABLED = 360


class SettlementTemplateStrategiesConstants:
"""Constants related to the configuration of settlement template strategies"""

INITIAL_BUYING_RATE = 0
FINAL_BUYING_RATE = 50
INITIAL_SELLING_RATE = 50
Expand Down
54 changes: 54 additions & 0 deletions src/gsy_e/gsy_e_core/non_p2p_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from gsy_framework.constants_limits import GlobalConfig
from gsy_framework.exceptions import GSyException


class NonP2PHandler:
"""Handles non-p2p case"""

def __init__(self, scenario: dict):
self.non_p2p_scenario = scenario
self._energy_sell_rate = 0.0
self._energy_buy_rate = 0.0
self._get_energy_rates_from_infinite_bus(scenario)
self._handle_non_p2p_scenario(scenario)

def _get_energy_rates_from_infinite_bus(self, scenario: dict):
for child in scenario["children"]:
if child.get("type") == "InfiniteBus":
self._energy_buy_rate = child.get("energy_buy_rate", GlobalConfig.FEED_IN_TARIFF)
self._energy_sell_rate = child.get(
"energy_sell_rate", GlobalConfig.MARKET_MAKER_RATE
)
return

raise GSyException(
"For non-p2p simulation, an InfiniteBus has to be present in the first "
"level of the configuration tree."
)

@staticmethod
def _is_home_area(area: dict):
return area.get("children") and all(
child.get("type", None) for child in area.get("children")
)

def _add_market_maker_to_home(self, area: dict):
if "children" not in area or not area["children"]:
return
if not self._is_home_area(area):
return
area["children"].append(
{
"name": "MarketMaker",
"type": "InfiniteBus",
"energy_buy_rate": self._energy_buy_rate,
"energy_sell_rate": self._energy_sell_rate,
}
)

def _handle_non_p2p_scenario(self, area: dict):
if "children" not in area or not area["children"]:
return
self._add_market_maker_to_home(area)
for child in area["children"]:
self._handle_non_p2p_scenario(child)
176 changes: 108 additions & 68 deletions src/gsy_e/gsy_e_core/rq_job_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,39 @@
from gsy_e.gsy_e_core.simulation import run_simulation
from gsy_e.gsy_e_core.util import update_advanced_settings
from gsy_e.models.config import SimulationConfig
from gsy_e.gsy_e_core.non_p2p_handler import NonP2PHandler

logging.getLogger().setLevel(logging.ERROR)
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)


# pylint: disable=too-many-branches, too-many-statements
def launch_simulation_from_rq_job(scenario: Dict,
settings: Optional[Dict],
events: Optional[str],
aggregator_device_mapping: Dict,
saved_state: Dict,
scm_properties: Dict,
job_id: str,
connect_to_profiles_db: bool = True):
def launch_simulation_from_rq_job(
scenario: Dict,
settings: Optional[Dict],
events: Optional[str],
aggregator_device_mapping: Dict,
saved_state: Dict,
scm_properties: Dict,
job_id: str,
connect_to_profiles_db: bool = True,
):
# pylint: disable=too-many-arguments, too-many-locals
"""Launch simulation from rq job."""

gsy_e.constants.CONFIGURATION_ID = scenario.pop("configuration_uuid", None)
try:
if not gsy_e.constants.CONFIGURATION_ID:
raise Exception("configuration_uuid was not provided")
raise Exception(
"configuration_uuid was not provided"
) # pylint disable=broad-exception-raised

logger.error("Starting simulation with job_id: %s and configuration id: %s",
job_id, gsy_e.constants.CONFIGURATION_ID)
logger.error(
"Starting simulation with job_id: %s and configuration id: %s",
job_id,
gsy_e.constants.CONFIGURATION_ID,
)

settings = _adapt_settings(settings)

Expand All @@ -47,61 +55,82 @@ def launch_simulation_from_rq_job(scenario: Dict,

_configure_constants_constsettings(scenario, settings, connect_to_profiles_db)

if gsy_e.constants.RUN_IN_NON_P2P_MODE:
scenario = NonP2PHandler(scenario).non_p2p_scenario

slot_length_realtime = (
duration(seconds=settings["slot_length_realtime"].seconds)
if "slot_length_realtime" in settings else None)
if "slot_length_realtime" in settings
else None
)

scenario_name = "json_arg"

kwargs = {"no_export": True,
"seed": settings.get("random_seed", 0)}
kwargs = {"no_export": True, "seed": settings.get("random_seed", 0)}

if ConstSettings.MASettings.MARKET_TYPE == SpotMarketTypeEnum.COEFFICIENTS.value:
kwargs.update({"scm_properties": scm_properties})

past_slots_sim_state = _handle_scm_past_slots_simulation_run(
scenario, settings, events, aggregator_device_mapping, saved_state, job_id,
scenario_name, slot_length_realtime, kwargs)
scenario,
settings,
events,
aggregator_device_mapping,
saved_state,
job_id,
scenario_name,
slot_length_realtime,
kwargs,
)

if past_slots_sim_state is not None:
saved_state = past_slots_sim_state
# Fake that the simulation is not in finished, but in running state in order to
# facilitate the state resume.
saved_state["general"]["sim_status"] = "running"

config = _create_config_settings_object(
scenario, settings, aggregator_device_mapping)
config = _create_config_settings_object(scenario, settings, aggregator_device_mapping)

if settings.get("type") == ConfigurationType.CANARY_NETWORK.value:
config.start_date = (
instance(
datetime.combine(date.today(), datetime.min.time()),
tz=gsy_e.constants.TIME_ZONE))
config.start_date = instance(
datetime.combine(date.today(), datetime.min.time()), tz=gsy_e.constants.TIME_ZONE
)

if ConstSettings.MASettings.MARKET_TYPE == SpotMarketTypeEnum.COEFFICIENTS.value:
config.start_date = config.start_date.subtract(hours=settings["scm"]
["scm_cn_hours_of_delay"])

run_simulation(setup_module_name=scenario_name,
simulation_config=config,
simulation_events=events,
redis_job_id=job_id,
saved_sim_state=saved_state,
slot_length_realtime=slot_length_realtime,
kwargs=kwargs)

logger.info("Finishing simulation with job_id: %s and configuration id: %s",
job_id, gsy_e.constants.CONFIGURATION_ID)
config.start_date = config.start_date.subtract(
hours=settings["scm"]["scm_cn_hours_of_delay"]
)

run_simulation(
setup_module_name=scenario_name,
simulation_config=config,
simulation_events=events,
redis_job_id=job_id,
saved_sim_state=saved_state,
slot_length_realtime=slot_length_realtime,
kwargs=kwargs,
)

logger.info(
"Finishing simulation with job_id: %s and configuration id: %s",
job_id,
gsy_e.constants.CONFIGURATION_ID,
)

# pylint: disable=broad-except
except Exception:
# pylint: disable=import-outside-toplevel
from gsy_e.gsy_e_core.redis_connections.simulation import publish_job_error_output
logger.error("Error on jobId, %s, configuration id: %s",
job_id, gsy_e.constants.CONFIGURATION_ID)

logger.error(
"Error on jobId, %s, configuration id: %s", job_id, gsy_e.constants.CONFIGURATION_ID
)
publish_job_error_output(job_id, traceback.format_exc())
logger.error("Error on jobId, %s, configuration id: %s: error sent to gsy-web",
job_id, gsy_e.constants.CONFIGURATION_ID)
logger.error(
"Error on jobId, %s, configuration id: %s: error sent to gsy-web",
job_id,
gsy_e.constants.CONFIGURATION_ID,
)
raise


Expand All @@ -119,19 +148,23 @@ def _adapt_settings(settings: Dict) -> Dict:


def _configure_constants_constsettings(
scenario: Dict, settings: Dict, connect_to_profiles_db: bool):
scenario: Dict, settings: Dict, connect_to_profiles_db: bool
):
assert isinstance(scenario, dict)

GlobalConfig.CONFIG_TYPE = settings.get("type")

if settings.get("type") == ConfigurationType.COLLABORATION.value:
gsy_e.constants.EXTERNAL_CONNECTION_WEB = True

if settings.get("type") in [ConfigurationType.CANARY_NETWORK.value,
ConfigurationType.B2B.value]:
if settings.get("type") in [
ConfigurationType.CANARY_NETWORK.value,
ConfigurationType.B2B.value,
]:
gsy_e.constants.EXTERNAL_CONNECTION_WEB = True
gsy_e.constants.RUN_IN_REALTIME = (
settings.get("type") == ConfigurationType.CANARY_NETWORK.value)
settings.get("type") == ConfigurationType.CANARY_NETWORK.value
)

if settings.get("type") == ConfigurationType.B2B.value:
ConstSettings.ForwardMarketSettings.ENABLE_FORWARD_MARKETS = True
Expand All @@ -149,30 +182,36 @@ def _configure_constants_constsettings(
if bid_offer_match_algo:
ConstSettings.MASettings.BID_OFFER_MATCH_TYPE = bid_offer_match_algo

ConstSettings.SettlementMarketSettings.RELATIVE_STD_FROM_FORECAST_FLOAT = (
settings.get(
"relative_std_from_forecast_percent",
ConstSettings.SettlementMarketSettings.RELATIVE_STD_FROM_FORECAST_FLOAT
))
ConstSettings.SettlementMarketSettings.RELATIVE_STD_FROM_FORECAST_FLOAT = settings.get(
"relative_std_from_forecast_percent",
ConstSettings.SettlementMarketSettings.RELATIVE_STD_FROM_FORECAST_FLOAT,
)

ConstSettings.SettlementMarketSettings.ENABLE_SETTLEMENT_MARKETS = settings.get(
"settlement_market_enabled",
ConstSettings.SettlementMarketSettings.ENABLE_SETTLEMENT_MARKETS
ConstSettings.SettlementMarketSettings.ENABLE_SETTLEMENT_MARKETS,
)
gsy_e.constants.CONNECT_TO_PROFILES_DB = connect_to_profiles_db

if settings.get("p2p_enabled", True) is False:
ConstSettings.MASettings.MIN_BID_AGE = gsy_e.constants.MIN_OFFER_BID_AGE_P2P_DISABLED
ConstSettings.MASettings.MIN_OFFER_AGE = gsy_e.constants.MIN_OFFER_BID_AGE_P2P_DISABLED
gsy_e.constants.RUN_IN_NON_P2P_MODE = True

if settings.get("scm"):
ConstSettings.SCMSettings.MARKET_ALGORITHM = CoefficientAlgorithm(
settings["scm"]["coefficient_algorithm"]).value
settings["scm"]["coefficient_algorithm"]
).value
ConstSettings.SCMSettings.GRID_FEES_REDUCTION = settings["scm"]["grid_fees_reduction"]
ConstSettings.SCMSettings.INTRACOMMUNITY_BASE_RATE_EUR = (
settings["scm"]["intracommunity_rate_base_eur"])
ConstSettings.SCMSettings.INTRACOMMUNITY_BASE_RATE_EUR = settings["scm"][
"intracommunity_rate_base_eur"
]
else:
assert spot_market_type is not SpotMarketTypeEnum.COEFFICIENTS.value


def _create_config_settings_object(
scenario: Dict, settings: Dict, aggregator_device_mapping: Dict
scenario: Dict, settings: Dict, aggregator_device_mapping: Dict
) -> SimulationConfig:

config_settings = {
Expand Down Expand Up @@ -202,13 +241,9 @@ def _create_config_settings_object(
"market_maker_rate": settings.get(
"market_maker_rate", ConstSettings.GeneralSettings.DEFAULT_MARKET_MAKER_RATE
),
"capacity_kW": settings.get(
"capacity_kW", ConstSettings.PVSettings.DEFAULT_CAPACITY_KW
),
"capacity_kW": settings.get("capacity_kW", ConstSettings.PVSettings.DEFAULT_CAPACITY_KW),
"grid_fee_type": settings.get("grid_fee_type", GlobalConfig.grid_fee_type),
"external_connection_enabled": settings.get(
"external_connection_enabled", False
),
"external_connection_enabled": settings.get("external_connection_enabled", False),
"aggregator_device_mapping": aggregator_device_mapping,
"hours_of_delay": settings.get("scm", {}).get(
"hours_of_delay", ConstSettings.SCMSettings.HOURS_OF_DELAY
Expand All @@ -222,10 +257,15 @@ def _create_config_settings_object(


def _handle_scm_past_slots_simulation_run(
scenario: Dict, settings: Optional[Dict], events: Optional[str],
aggregator_device_mapping: Dict, saved_state: Dict, job_id: str,
scenario_name: str, slot_length_realtime: Optional[duration],
kwargs: Dict
scenario: Dict,
settings: Optional[Dict],
events: Optional[str],
aggregator_device_mapping: Dict,
saved_state: Dict,
job_id: str,
scenario_name: str,
slot_length_realtime: Optional[duration],
kwargs: Dict,
) -> Optional[Dict]:
# pylint: disable=too-many-arguments
"""
Expand All @@ -243,16 +283,15 @@ def _handle_scm_past_slots_simulation_run(
settings_copy = deepcopy(settings)

config = _create_config_settings_object(
scenario_copy, settings_copy, aggregator_device_mapping)
scenario_copy, settings_copy, aggregator_device_mapping
)
# We are running SCM Canary Networks with some days of delay compared to realtime in order to
# compensate for delays in transmission of the asset measurements.
# Adding 4 hours of extra time to the SCM past slots simulation duration, in order to
# compensate for the runtime of the SCM past slots simulation and to not have any results gaps
# after this simulation run and the following Canary Network launch.
config.end_date = (
now(tz=gsy_e.constants.TIME_ZONE)
.subtract(hours=config.hours_of_delay)
.add(hours=4)
now(tz=gsy_e.constants.TIME_ZONE).subtract(hours=config.hours_of_delay).add(hours=4)
)
config.sim_duration = config.end_date - config.start_date
GlobalConfig.sim_duration = config.sim_duration
Expand All @@ -264,6 +303,7 @@ def _handle_scm_past_slots_simulation_run(
redis_job_id=job_id,
saved_sim_state=saved_state,
slot_length_realtime=slot_length_realtime,
kwargs=kwargs)
kwargs=kwargs,
)
gsy_e.constants.RUN_IN_REALTIME = True
return simulation_state
Loading

0 comments on commit 768ecfd

Please sign in to comment.