diff --git a/SAS/TMSS/src/tmss/tmssapp/subtasks.py b/SAS/TMSS/src/tmss/tmssapp/subtasks.py index d28063a16409f8d82a8ac12ddd6a3c6fd9882c96..fad3ee9ccdd8d07d0801cb8fc630e52fd8989226 100644 --- a/SAS/TMSS/src/tmss/tmssapp/subtasks.py +++ b/SAS/TMSS/src/tmss/tmssapp/subtasks.py @@ -31,13 +31,11 @@ def create_subtasks_from_task_blueprint(task_blueprint: TaskBlueprint) -> [Subta check_prerequities_for_subtask_creation(task_blueprint) # fixed mapping from template name to generator functions which create the list of subtask(s) for this task_blueprint - generators_mapping = {'observation schema': [create_observation_control_subtask_from_task_blueprint_target_observation, + generators_mapping = {'observation schema': [create_observation_control_subtask_from_task_blueprint, create_qafile_subtask_from_task_blueprint, create_qaplots_subtask_from_task_blueprint], - 'calibrator schema': [create_observation_control_subtask_from_task_blueprint_calibrator_observation, - create_qafile_subtask_from_task_blueprint, - create_qaplots_subtask_from_task_blueprint], 'preprocessing schema': [create_preprocessing_subtask_from_task_blueprint]} + generators_mapping['calibrator schema'] = generators_mapping['observation schema'] template_name = task_blueprint.specifications_template.name if template_name in generators_mapping: @@ -55,144 +53,112 @@ def create_subtasks_from_task_blueprint(task_blueprint: TaskBlueprint) -> [Subta raise SubtaskCreationException('Cannot create subtasks for task id=%s because no generator exists for its schema name=%s' % (task_blueprint.pk, template_name)) -def get_station_specifications_from_observation_blueprint(task_spec): +def create_observation_subtask_specifications_from_observation_task_blueprint(task_blueprint: TaskBlueprint) -> (dict, SubtaskTemplate): """ - Retrieve the specification from (Target) Observation and return it into a subtask observation - stations json structure + Create a valid observation subtask specification ('observationcontrol schema' SubtaskTemplate schema) based on the task_blueprint's settings """ - lst_digital_pointings = [] - if "SAPs" in task_spec: - for sap in task_spec["SAPs"]: - lst_digital_pointings.append( - {"name": sap["name"], - "pointing": {"direction_type": sap["digital_pointing"]["direction_type"], - "angle1": sap["digital_pointing"]["angle1"], - "angle2": sap["digital_pointing"]["angle2"]}, - "subbands": sap["subbands"] - }) + + # check if task_blueprint has an observation-like specification + if task_blueprint.specifications_template.name.lower() not in ['observation schema', 'calibrator schema']: + raise SubtaskCreationException("Cannot create observation subtask specifications from task_blueprint id=%s with template name='%s'" % ( + task_blueprint.id, task_blueprint.specifications_template.name)) + + # start with an observation subtask specification with all the defaults and the right structure according to the schema + subtask_template = SubtaskTemplate.objects.get(name='observationcontrol schema') + subtask_spec = get_default_json_object_for_schema(subtask_template.schema) + + # wipe the default pointings, these should come from the task_spec + subtask_spec['stations']['analog_pointing'] = {} + subtask_spec['stations']['digital_pointings'] = [] + + # now go over the settings in the task_spec and 'copy'/'convert' them to the subtask_spec + task_spec = task_blueprint.specifications_doc + + # The calibrator has a minimal calibration-specific specification subset. + # The rest of it's specs are 'shared' with the target observation. + # So... copy the calibrator specs first, then loop over the shared target/calibrator specs... + if 'calibrator' in task_blueprint.specifications_template.name.lower(): + subtask_spec['stations']['analog_pointing'] = {"direction_type": task_spec["pointing"]["direction_type"], + "angle1": task_spec["pointing"]["angle1"], + "angle2": task_spec["pointing"]["angle2"]} + # for the calibrator, the digital pointing is equal to the analog pointing + subtask_spec['stations']['digital_pointings'] = [ {'name': 'calibrator', # there is no name for the calibrator pointing in the task spec + 'subbands': list(range(0,488)), # there are no subbands for the calibrator pointing in the task spec + 'pointing': subtask_spec['stations']['analog_pointing'] } ] + + if task_spec.get('autoselect', False): + logger.info("auto-selecting calibrator target based on elevation of target observation...") + # what to do? overrive the pointing??? + #TODO: implement + + target_task_blueprint = get_related_target_observation_task_blueprint(task_blueprint) + if target_task_blueprint is None: + raise SubtaskCreationException("Cannot create calibrator observation subtask specifications from task_blueprint id=%s with template name='%s' because no related target observation task_blueprint is found" % ( + task_blueprint.id, task_blueprint.specifications_template.name)) + + task_spec = target_task_blueprint.specifications_doc + logger.info("Using station and correlator settings for calibrator observation task_blueprint id=%s from target observation task_blueprint id=%s", task_blueprint.id, target_task_blueprint.id) + + subtask_spec['stations']["antenna_set"] = task_spec["antenna_set"] + subtask_spec['stations']["filter"] = task_spec["filter"] if "stations" in task_spec: if "group" in task_spec["stations"][0]: # retreive current available stations in group from lcuhead station_group = task_spec["stations"][0]["group"] - station_list = get_current_stations(station_group, as_host_names=False) + subtask_spec['stations']['station_list'] = get_current_stations(station_group, as_host_names=False) else: - station_list = task_spec["stations"] + subtask_spec['stations']['station_list'] = task_spec["stations"] - # Apparently analog pointing is not allways required - if "tile_beam" in task_spec: - analog_pointing = {"direction_type": task_spec["tile_beam"]["direction_type"], - "angle1": task_spec["tile_beam"]["angle1"], - "angle2": task_spec["tile_beam"]["angle2"] - } - else: - analog_pointing = {} - - subtask_stations_spec = {"stations": {"station_list": station_list, - "antenna_set": task_spec["antenna_set"], - "filter": task_spec["filter"], - "analog_pointing": analog_pointing, - "digital_pointings": lst_digital_pointings - } - } - else: - subtask_stations_spec = {} - - return subtask_stations_spec - - -def create_observation_control_subtask_from_task_blueprint_target_observation(task_blueprint: TaskBlueprint) -> Subtask: - """ - Create an observation control subtask from task which is a Target Observation. - Determine the non-default specifications which is required for the observation control subtasks. - Therefore translate Task to SubTask settings - See https://support.astron.nl/confluence/pages/viewpage.action?spaceKey=TMSS&title=UC1+JSON - - Each Target Observation Task is split into Subtasks as follows: - * An ObsControl Subtask is created to configure the stations & COBALT: - * If any Calibrator Addon Tasks refer to this Target Observation Task using a parallel Task Scheduling Relation, - a Calibrator station beam is added to the specification. - * Subtasks are created for generating ADDR inspection plots on any correlated visibilities produced. - - :param task_blueprint: - :return: subtask - """ - logger.info("Task (id=%d) is a Target Observation" % task_blueprint.pk) - # TODO: should angle 3 als be handled by observation control? - # What todo with COBALT settings where should that come from? - # How should the observation duration fit in observation control task? - - subtask_specifications_doc = get_station_specifications_from_observation_blueprint(task_blueprint.specifications_doc) - subtask = create_observation_control_subtask_from_task_blueprint(task_blueprint, subtask_specifications_doc) - # Adjust only the channels for correlator - if "correlator" in task_blueprint.specifications_doc: - subtask.specifications_doc["COBALT"]["correlator"]["channels_per_subband"] = \ - task_blueprint.specifications_doc["correlator"]["channels_per_subband"] - return subtask + if 'calibrator' not in task_blueprint.specifications_template.name.lower(): + # copy/convert the analoge/digital_pointings only for non-calibrator observations (the calibrator has its own pointing) + for sap in task_spec.get("SAPs", []): + subtask_spec['stations']['digital_pointings'].append( + {"name": sap["name"], + "pointing": {"direction_type": sap["digital_pointing"]["direction_type"], + "angle1": sap["digital_pointing"]["angle1"], + "angle2": sap["digital_pointing"]["angle2"]}, + "subbands": sap["subbands"] + }) + if "tile_beam" in task_spec: + subtask_spec['stations']['analog_pointing'] = { "direction_type": task_spec["tile_beam"]["direction_type"], + "angle1": task_spec["tile_beam"]["angle1"], + "angle2": task_spec["tile_beam"]["angle2"] } -def create_observation_control_subtask_from_task_blueprint_calibrator_observation(task_blueprint: TaskBlueprint) -> Subtask: - """ - Create an observation control subtask from task which is a Calibrator Observation. - Determine the non-default specifications which is required for the observation control subtasks. - Therefore translate Task to SubTask settings - See https://support.astron.nl/confluence/pages/viewpage.action?spaceKey=TMSS&title=UC1+JSON + if "correlator" in task_spec: + subtask_spec["COBALT"]["correlator"]["channels_per_subband"] = task_spec["correlator"]["channels_per_subband"] - Each Calibrator Addon Task, which has a before or after relation with an Observation Task, - is turned into the same set of Subtasks: - * An ObsControl Subtask is created, with a beam pointing at the calibrator source. - All other station & correlator settings are copied from the referenced Observation Task, - * Subtasks are created for generating ADDR inspection plots on any correlated visibilities produced. + # TODO: compute remaining subtask correlator settings from task_spec ? + # subtask_spec["COBALT"]["correlator"]["integration_time"] = task_spec["correlator"]["integration_time"] + # subtask_spec["COBALT"]["correlator"]["storage_cluster"] = task_spec["correlator"]["storage_cluster"] - :param task_blueprint: - :return: subtask - """ - logger.info("Task (id=%d) is a Calibrator Addon Observation" % task_blueprint.pk) - # Get the related Target Observation Task - related_blueprint_target_observation = get_related_scheduling_blueprint_task(task_blueprint) - if related_blueprint_target_observation is None: - raise SubtaskCreationException('Cannot create observation control subtasks for task id=%s because no related scheduling blueprint task exists.' - ' This is required for a calibrator observation' % task_blueprint.pk) - else: - logger.info("Related scheduling blueprint task found id=%d", related_blueprint_target_observation.pk) - - calibrator_add_on_spec = task_blueprint.specifications_doc - subtask_specifications_doc = get_station_specifications_from_observation_blueprint(related_blueprint_target_observation.specifications_doc) - # Retrieve pointings from the original calibrator AddOn specifications - subtask_specifications_doc["stations"]["analog_pointing"] = { - "direction_type": calibrator_add_on_spec["pointing"]["direction_type"], - "angle1": calibrator_add_on_spec["pointing"]["angle1"], - "angle2": calibrator_add_on_spec["pointing"]["angle2"]} - subtask = create_observation_control_subtask_from_task_blueprint(task_blueprint, subtask_specifications_doc) - # Adjust only the channels for correlator - if "correlator" in task_blueprint.specifications_doc: - subtask.specifications_doc["COBALT"]["correlator"]["channels_per_subband"] = \ - related_blueprint_target_observation.specifications_doc["correlator"]["channels_per_subband"] - logger.info("Subtask %d created" % subtask.pk) - return subtask + return subtask_spec, subtask_template -def get_related_scheduling_blueprint_task(task_blueprint): +def get_related_target_observation_task_blueprint(calibrator_task_blueprint: TaskBlueprint) -> TaskBlueprint: """ - Retrieve the related scheduling blueprint task object + get the related target observation task_blueprint for the given calibrator task_blueprint if nothing found return None - :param task_blueprint: - :return: related_scheduling_task_blueprint """ - related_scheduling_task_blueprint = None + if 'calibrator' not in calibrator_task_blueprint.specifications_template.name.lower(): + raise ValueError("Cannot get a related target observation task_blueprint for non-calibrator task_blueprint id=%s template_name='%s'", + calibrator_task_blueprint.id, calibrator_task_blueprint.specifications_template.name) + try: - related_scheduling = TaskSchedulingRelationBlueprint.objects.get(first=task_blueprint) - related_scheduling_task_blueprint = related_scheduling.second - except TaskSchedulingRelationBlueprint.DoesNotExist: + return next(relation.second for relation in TaskSchedulingRelationBlueprint.objects.filter(first=calibrator_task_blueprint).all() + if relation.second is not None and relation.second.specifications_template.name.lower() == 'observation schema') + except StopIteration: try: - related_scheduling = TaskSchedulingRelationBlueprint.objects.get(second=task_blueprint) - related_scheduling_task_blueprint = related_scheduling.first - except TaskSchedulingRelationBlueprint.DoesNotExist: - logger.info("No related scheduling blueprint task found for id=%d", task_blueprint.pk) - return related_scheduling_task_blueprint + return next(relation.first for relation in TaskSchedulingRelationBlueprint.objects.filter(second=calibrator_task_blueprint).all() + if relation.first is not None and relation.first.specifications_template.name.lower() == 'observation schema') + except StopIteration: + logger.info("No related target observation task_blueprint found for calibrator observation task_blueprint id=%d", calibrator_task_blueprint.id) + + return None -def create_observation_control_subtask_from_task_blueprint(task_blueprint: TaskBlueprint, extra_specifications_doc) -> Subtask: +def create_observation_control_subtask_from_task_blueprint(task_blueprint: TaskBlueprint) -> Subtask: """ Create an observation control subtask . This method implements "Instantiate subtasks" step from the "Specification Flow" @@ -202,10 +168,7 @@ def create_observation_control_subtask_from_task_blueprint(task_blueprint: TaskB check_prerequities_for_subtask_creation(task_blueprint) # step 1: create subtask in defining state - subtask_template = SubtaskTemplate.objects.get(name='observationcontrol schema') - - # This is some 'extra' specification to add to subtask depended on observation type - specifications_doc = add_defaults_to_json_object_for_schema(extra_specifications_doc, subtask_template.schema) + specifications_doc, subtask_template = create_observation_subtask_specifications_from_observation_task_blueprint(task_blueprint) cluster_name = task_blueprint.specifications_doc.get("storage_cluster", "CEP4") subtask_data = { "start_time": None, "stop_time": None,