def run(self): """Run the co-simulation This method deploys and run the co-simulation and waits for manual nodes to bun launched. >>> from zerobnl import CoSim >>> sim = CoSim() --> Create complete co-simulation graph (meta-models, environments, nodes and links) with simulation sequence >>> sim.run() """ self._create_and_fill_folders_to_mount_into_nodes() logger.debug("CREATE NODES") self._create_and_fill_orchestrator_folder() logger.debug("CREATE ORCH") self._launch_redis_and_docker_network() logger.debug("LAUNCH NETWORK+REDIS") nodes_to_run = self.nodes.loc[self.nodes["Local"] == False].index create_full_yaml(nodes_to_run) logger.debug("DUMP YAML") local_nodes = self.nodes.loc[self.nodes["Local"] == True] for node in local_nodes.index: logger.warning("Local node to run in [{}] > python {}".format( os.path.join(TEMP_FOLDER, node.lower()), NODE_WRAP_FILE)) if len(local_nodes) > 0: logger.info("Waiting for local nodes to run...") start = time() self._docker_compose_up() stop = time() - start logger.info("Simulation finished in {} min and {} sec".format( int(stop // 60), int(round(stop % 60, 0)))) logger.debug("FINISHED PROCESS")
def run(self): self.wait_for_nodes(sum(self.sequence)) for idx, step in enumerate(self.steps): logger.debug("STEP {}/{}".format(idx + 1, len(self.steps))) for idx_group in range(len(self.sequence)): self.send_current_state_for_update() self.make_step(idx_group, step) self.pub.send_string("{} | {} | {}".format("ALL", "END", 0))
def _update_inputs(self, state): """ :param state: """ logger.debug("INPUTS {}".format(state)) for key, value in state.items(): if key in self.input_map: self.set_attribute(self.input_map[key], value)
def update_attributes(self, step, grp, nbr): """The update_attributes() method is used to send to nodes the last state of the complete system in order for the nodes to update theri inputs.""" logger.debug("ORCH -> GRP {}, UPDATE {}".format(grp, step)) self._pub.send_string("{} | {} | {}".format(grp, "UPDATE", self._state)) ack = 0 while ack < nbr: self._receiver.recv_string() ack += 1
def __init__(self, sequence, steps, port_pub, port_pull): self._sequence = sequence # [["GRP1", 2], ["GRP2", 1]] self._steps = steps # [60, 60, 60, 60] (in sec) self._state = {} logger.debug("ORCH -> created") self._pub = self.CONTEXT.socket(zmq.PUB) self._pub.bind("tcp://*:{}".format(port_pub)) logger.debug("ORCH -> PUB to {}".format(port_pub)) self._receiver = self.CONTEXT.socket(zmq.PULL) self._receiver.bind("tcp://*:{}".format(port_pull)) logger.debug("ORCH -> PULL to {}".format(port_pull))
def make_step(self, step, unit, grp, nbr): """The make_step() method allow to make a simulation step for a group, and used at each step for each group defined in the simulation sequence.""" logger.debug("ORCH -> GRP {}, STEP {}".format(grp, step)) self._pub.send_string("{} | {} | {}:{}".format(grp, "STEP", step, unit)) ack = 0 while ack < nbr: string = self._receiver.recv_string() node, _, value = string.split(" | ") node_state = ast.literal_eval(value) if node not in self._state.keys(): self._state[node] = {} for attr, value in node_state.items(): self._state[node][attr] = value ack += 1 logger.debug("ORCH -> STEP {} {}/{}".format(step, ack, nbr)) logger.debug("STATE: {}".format(self._state))
def run(self): """ """ for attr, value in self.init_values.items(): self.set_attribute(attr, value) self.sender.send_string("{}".format(self.name)) logger.debug("INIT VALUES DONE") while True: string = self.sub.recv_string() grp, act, value = string.split(" | ") if act == "UPDATE": logger.debug("UPDATE") self._update_inputs(ast.literal_eval(value)) self.sender.send_string("{} | Update | Done".format(self.name)) logger.debug("UPDATE DONE") elif act == "STEP": logger.debug("STEP") self.step(float(value)) state = {o: self.get_attribute(o) for o in self.outputs} for attr, value in state.items(): self._send_attribute_value_to_results_db(attr, value, opt="OUT") logger.debug("STATE {}".format(state)) self.sender.send_string("{} | {} | {}".format(self.name, "STATE", state)) logger.debug("STEP DONE") elif act == "END": self.exit() break
def exit(self): """[TO OVERRIDE (if an exit action is needed)] The exit() method is called to properly close the model""" logger.debug("EXIT")
def step(self, value): """[TO OVERRIDE] The step() method is called to make a step with the model with a given step size and unit.""" self.real_time += pd.DateOffset(**{self.time_unit: value}) self.simu_time += value logger.debug("NEW TIME")
def get_attribute(self, attr): """[TO OVERRIDE] The get_attribute() method is called to get the value of an attribute of the model.""" logger.debug("GET ATTRIBUTE {}".format(attr))
def set_attribute(self, attr, value): """[TO OVERRIDE] The set_attribute() method is called to set an attribute of the model to a given value.""" logger.debug("SET ATTRIBUTE {} to {}".format(attr, value))
def __init__(self): with open(NODE_CONFIG_FILE) as fp: config = json.load(fp) logger.debug("LOAD CONFIG NODE") self.name = config["NAME"] self.group = config["GROUP"] self.local = config["LOCAL"] self.input_map = ast.literal_eval(config["INPUT_MAP"]) self.outputs = config["OUTPUTS"] self.init_values = config["INIT_VALUES"] self.parameters = config["PARAMETERS"] self.real_time = pd.to_datetime(config["START"]) self.time_unit = config["TIME_UNIT"] self.simu_time = 0.0 redis_host = {True: "localhost", False: REDIS_HOST_NAME}[self.local] self.redis = redis.StrictRedis(host=redis_host, port=REDIS_PORT, db=0) logger.debug("CONNECT REDIS") self.sub = zmq.Context().socket(zmq.SUB) self.sender = zmq.Context().socket(zmq.PUSH) logger.debug("CREATE SUB/SEND") zero_host = {True: "localhost", False: ORCH_HOST_NAME}[self.local] self.sub.connect("tcp://{}:{}".format(zero_host, PORT_PUB_SUB)) self.sender.connect("tcp://{}:{}".format(zero_host, PORT_PUSH_PULL)) logger.debug("CONNECT TO SUB/SEND") self.sub.setsockopt_string(zmq.SUBSCRIBE, self.group) self.sub.setsockopt_string(zmq.SUBSCRIBE, "ALL") logger.debug("SUBSCRIBED TO GRP/ALL") logger.debug("INIT DONE")