Skip to content

Commit 33cb2c4

Browse files
committed
Add sensitivity analysis tasks
1 parent f88555c commit 33cb2c4

3 files changed

Lines changed: 228 additions & 1 deletion

File tree

task_queue/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ celery
22
flower
33
redis
44
python-multipart
5+
jsonschema
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import jsonschema
2+
import traceback
3+
from multi_vector_simulator.utils.data_parser import MAP_EPA_MVS
4+
5+
6+
SA_SCHEMA = {
7+
"type": "object",
8+
"required": [
9+
"variable_parameter_name",
10+
"variable_parameter_range",
11+
"variable_parameter_ref_val",
12+
"output_parameter_names",
13+
],
14+
"properties": {
15+
"variable_parameter_name": {
16+
"oneOf": [{"type": "array", "items": {"type": "string"}}]
17+
},
18+
"variable_parameter_range": {"type": "array", "items": {"type": "number"}},
19+
"variable_parameter_ref_val": {"type": "number"},
20+
"output_parameter_names": {"type": "array", "items": {"type": "string"}},
21+
},
22+
"additionalProperties": False,
23+
}
24+
25+
SENSITIVITY_ANALYSIS_SETTINGS = "sensitivity_analysis_settings"
26+
27+
28+
class SensitivityAnalysis:
29+
__raw_input = None
30+
variable_parameter_name = None
31+
variable_parameter_range = None
32+
variable_parameter_ref_val = None
33+
output_parameter_names = None
34+
validation_error = ""
35+
36+
def __init__(self, dict_settings):
37+
sa_settings = dict_settings.get(SENSITIVITY_ANALYSIS_SETTINGS, None)
38+
self.__raw_input = sa_settings
39+
if sa_settings is not None:
40+
try:
41+
jsonschema.validate(sa_settings, SA_SCHEMA)
42+
schema_is_valid = True
43+
except jsonschema.exceptions.ValidationError as e:
44+
schema_is_valid = False
45+
self.validation_error = "{}".format(traceback.format_exc())
46+
47+
if schema_is_valid is True:
48+
self.variable_parameter_name = sa_settings.get(
49+
"variable_parameter_name", None
50+
)
51+
self.format_parameter_name()
52+
53+
self.variable_parameter_range = sa_settings.get(
54+
"variable_parameter_range", None
55+
)
56+
self.output_parameter_names = sa_settings.get(
57+
"output_parameter_names", None
58+
)
59+
self.variable_parameter_ref_val = sa_settings.get(
60+
"variable_parameter_ref_val", None
61+
)
62+
else:
63+
self.validation_error = (
64+
f"The key {SENSITIVITY_ANALYSIS_SETTINGS} is missing in the input json"
65+
)
66+
67+
def format_parameter_name(self):
68+
if self.variable_parameter_name is not None:
69+
self.variable_parameter_name = tuple(
70+
[MAP_EPA_MVS.get(key, key) for key in self.variable_parameter_name]
71+
)
72+
73+
def is_valid(self):
74+
if (
75+
self.variable_parameter_name is not None
76+
and self.variable_parameter_range is not None
77+
and self.output_parameter_names is not None
78+
and self.variable_parameter_ref_val
79+
):
80+
answer = True
81+
if self.variable_parameter_ref_val not in self.variable_parameter_range:
82+
answer = False
83+
self.validation_error = (
84+
f"The value ({self.variable_parameter_ref_val}) of the variable parameter"
85+
f" {'.'.join(self.variable_parameter_name)} for the reference scenario of"
86+
" the sensitivity analysis is not within the variable range provided"
87+
f": [{', '.join([str(p) for p in self.variable_parameter_range]) }]"
88+
)
89+
else:
90+
answer = False
91+
return answer
92+
93+
def __str__(self):
94+
return str(self.__raw_input)
95+
96+
97+
if __name__ == "__main__":
98+
import json
99+
100+
with open("test_sa.json", "r") as jf:
101+
input_dict = json.load(jf)
102+
103+
# with open("AFG_epa_format.json", "w") as jf:
104+
# json.dump(input_dict, jf, indent=4)
105+
106+
# input_dict[SENSITIVITY_ANALYSIS_SETTINGS] = {
107+
# "variable_parameter_name": ["energy_busses"],
108+
# "variable_parameter_range": [1, 2, 3.2, 3.5],
109+
# "variable_parameter_ref_val": 3,
110+
# "output_parameter_names": [
111+
# "specific_emissions_per_electricity_equivalent",
112+
# "total_feedinElectricity",
113+
# "total_internal_generation",
114+
# "peak_flow",
115+
# ],
116+
# }
117+
sa = SensitivityAnalysis(input_dict)
118+
print(sa.is_valid(), sa.validation_error)
119+
import ipdb
120+
121+
ipdb.set_trace()
122+
# input_dict[SENSITIVITY_ANALYSIS_SETTINGS] = {
123+
# "variable_parameter_name": [
124+
# "energy_providers",
125+
# "Grid_DSO",
126+
# "energy_price",
127+
# "value",
128+
# ],
129+
# "variable_parameter_range": [1, 2, 3, 3.5],
130+
# "variable_parameter_ref_val": 3,
131+
# "output_parameter_names": [
132+
# "specific_emissions_per_electricity_equivalent",
133+
# "total_feedinElectricity",
134+
# "total_internal_generation",
135+
# "peak_flow",
136+
# ],
137+
# # }
138+
# sa = SensitivityAnalysis(input_dict)
139+
# print(sa.is_valid(), sa.validation_error)
140+
# sa = SensitivityAnalysis({SENSITIVITY_ANALYSIS_SETTINGS: {
141+
# "variable_parameter_name": "",
142+
# "variable_parameter_range": "",
143+
# "variable_parameter_ref_val": "",
144+
# "output_parameter_names": "",
145+
# }})

task_queue/tasks.py

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
import os
22
import time
3+
import logging
34
import traceback
45
import json
56
from copy import deepcopy
67
from celery import Celery
78

8-
from multi_vector_simulator.server import run_simulation as mvs_simulation
9+
from multi_vector_simulator.server import (
10+
run_simulation as mvs_simulation,
11+
run_sensitivity_analysis_step as mvs_sensitivity_analysis_step,
12+
)
13+
from multi_vector_simulator.utils import set_nested_value, nested_dict_crawler
914
from multi_vector_simulator.utils.data_parser import convert_epa_params_to_mvs
1015

16+
from sensitivity_analysis_utils import SensitivityAnalysis
17+
1118
CELERY_BROKER_URL = (os.environ.get("CELERY_BROKER_URL", "redis://localhost:6379"),)
1219
CELERY_RESULT_BACKEND = os.environ.get(
1320
"CELERY_RESULT_BACKEND", "redis://localhost:6379"
@@ -34,3 +41,77 @@ def run_simulation(simulation_input: dict,) -> dict:
3441
INPUT_JSON_MVS=dict_values,
3542
)
3643
return json.dumps(simulation_output)
44+
45+
46+
@app.task(name=f"{CELERY_TASK_NAME}.run_sensitivity_analysis")
47+
def run_sensitivity_analysis(simulation_input: dict,):
48+
epa_json = deepcopy(simulation_input)
49+
50+
# parse the sensitivity analysis settings from input json
51+
sa_settings = SensitivityAnalysis(epa_json)
52+
53+
if sa_settings.is_valid() is False:
54+
answer = dict(
55+
SERVER=CELERY_TASK_NAME,
56+
ERROR="{}".format(sa_settings.validation_error),
57+
INPUT_JSON_EPA=epa_json,
58+
SENSITIVITY_ANALYSIS_SETTINGS=str(sa_settings),
59+
)
60+
else:
61+
mvs_dict = None
62+
param_val = None
63+
64+
# start a mvs simulation for the reference scenario
65+
reference_simulation = run_simulation.apply_async(
66+
args=[epa_json], queue=CELERY_TASK_NAME
67+
)
68+
answer = dict(ref_sim_id=reference_simulation.id, sensitivity_analysis_ids=[])
69+
70+
try:
71+
mvs_dict = convert_epa_params_to_mvs(epa_json)
72+
task_ids = []
73+
# perform one mvs sensitivity analysis step per value of the variable parameter
74+
# this is similar to running a simulation, only part of the output is returned
75+
for i, param_val in enumerate(sa_settings.variable_parameter_range):
76+
77+
modified_mvs_dict = set_nested_value(
78+
mvs_dict, param_val, sa_settings.variable_parameter_name
79+
)
80+
result = run_sensitivity_analysis_step.apply_async(
81+
args=[modified_mvs_dict, i, sa_settings.output_parameter_names],
82+
queue=CELERY_TASK_NAME,
83+
)
84+
task_ids.append(result.id)
85+
answer["sensitivity_analysis_ids"] = task_ids
86+
87+
except Exception:
88+
answer["sensitivity_analysis_ids"] = dict(
89+
SERVER=CELERY_TASK_NAME,
90+
ERROR="{}".format(traceback.format_exc()),
91+
INPUT_PARAM_PATH=sa_settings.variable_parameter_name,
92+
INPUT_PARAM_VAL=param_val,
93+
PARAM_PATHES=nested_dict_crawler(mvs_dict),
94+
)
95+
96+
return answer
97+
98+
99+
@app.task(name=f"{CELERY_TASK_NAME}.run_sensitivity_analysis_step")
100+
def run_sensitivity_analysis_step(
101+
mvs_dict: dict, step_idx: int, output_variables: list
102+
) -> dict:
103+
mvs_dict
104+
try:
105+
simulation_output = mvs_sensitivity_analysis_step(
106+
mvs_dict, step_idx, output_variables
107+
)
108+
simulation_output["SERVER"] = CELERY_TASK_NAME
109+
except Exception as e:
110+
simulation_output = dict(
111+
SERVER=CELERY_TASK_NAME,
112+
ERROR="{}".format(traceback.format_exc()),
113+
step_idx=step_idx,
114+
INPUT_JSON_MVS=mvs_dict,
115+
OUTPUT_VARIABLES=output_variables,
116+
)
117+
return json.dumps(simulation_output)

0 commit comments

Comments
 (0)