Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/gsye 766 #1798

Merged
merged 5 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now this is good. However, keep in mind that in the future we might need to set different energy rates per home. We will deal with it when we need it though, nothing to do for now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, having different rates per home will lead to more changes anyway. Let's deal with this then. Thanks for pointing out though.

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(

Check warning on line 24 in src/gsy_e/gsy_e_core/non_p2p_handler.py

View check run for this annotation

Codecov / codecov/patch

src/gsy_e/gsy_e_core/non_p2p_handler.py#L24

Added line #L24 was not covered by tests
"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

Check warning on line 37 in src/gsy_e/gsy_e_core/non_p2p_handler.py

View check run for this annotation

Codecov / codecov/patch

src/gsy_e/gsy_e_core/non_p2p_handler.py#L37

Added line #L37 was not covered by tests
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 @@

_configure_constants_constsettings(scenario, settings, connect_to_profiles_db)

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

Check warning on line 59 in src/gsy_e/gsy_e_core/rq_job_handler.py

View check run for this annotation

Codecov / codecov/patch

src/gsy_e/gsy_e_core/rq_job_handler.py#L59

Added line #L59 was not covered by tests

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 _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 @@
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

Check warning on line 199 in src/gsy_e/gsy_e_core/rq_job_handler.py

View check run for this annotation

Codecov / codecov/patch

src/gsy_e/gsy_e_core/rq_job_handler.py#L197-L199

Added lines #L197 - L199 were not covered by tests

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 @@
"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 _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 @@
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 @@
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
Loading