From 026ecbe416a600124ea793ef02314aa29156db2c Mon Sep 17 00:00:00 2001
From: Jorrit Schaap <schaap@astron.nl>
Date: Wed, 11 Oct 2023 20:34:24 +0200
Subject: [PATCH] TMSS-2809: removed 'placed' status. For simplicity, just
 schedule everything.

---
 .../lib/postgres_listener.py                  |  10 -
 .../services/scheduling/lib/constraints.py    |   3 -
 .../scheduling/lib/dynamic_scheduling.py      | 217 ++++++------------
 .../scheduling/test/t_dynamic_scheduling.py   |  76 +++---
 .../websocket/lib/websocket_service.py        |   5 -
 .../src/tmss/tmssapp/adapters/plots.py        |  18 +-
 ...0_remove_schedulingunitblueprint_placed.py |  17 ++
 .../src/tmss/tmssapp/models/specification.py  |   1 -
 .../tmss/tmssapp/serializers/specification.py |   3 +-
 SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py |  17 +-
 SAS/TMSS/backend/src/tmss/tmssapp/tasks.py    |  27 +--
 .../tmss/tmssapp/viewsets/specification.py    |   6 +-
 .../test/acceptance_test/acceptance_test.py   |  12 +-
 SAS/TMSS/client/lib/tmssbuslistener.py        |   8 -
 .../helpers/timeline.renderer.helper.js       |   2 -
 .../src/services/schedule.service.js          |   1 -
 .../tmss_webapp/src/utils/ui.constants.js     |   1 -
 17 files changed, 150 insertions(+), 274 deletions(-)
 create mode 100644 SAS/TMSS/backend/src/tmss/tmssapp/migrations/0050_remove_schedulingunitblueprint_placed.py

diff --git a/SAS/TMSS/backend/services/postgres_listener/lib/postgres_listener.py b/SAS/TMSS/backend/services/postgres_listener/lib/postgres_listener.py
index 395ea36da6c..619b78367c7 100644
--- a/SAS/TMSS/backend/services/postgres_listener/lib/postgres_listener.py
+++ b/SAS/TMSS/backend/services/postgres_listener/lib/postgres_listener.py
@@ -125,9 +125,6 @@ class TMSSPGListener(PostgresListener):
         self.executeQuery(makePostgresNotificationQueries('', 'tmssapp_schedulingunitblueprint', 'update', columns=['rank']))
         self.subscribe('tmssapp_schedulingunitblueprint_update_rank', self.onSchedulingUnitBlueprintRankUpdated)
 
-        self.executeQuery(makePostgresNotificationQueries('', 'tmssapp_schedulingunitblueprint', 'update', columns=['placed']))
-        self.subscribe('tmssapp_schedulingunitblueprint_update_placed', self.onSchedulingUnitBlueprintPlacedUpdated)
-
 
 
         # SchedulingConstraintsWeightFactor
@@ -306,13 +303,6 @@ class TMSSPGListener(PostgresListener):
         self._sendNotification(TMSS_SCHEDULINGUNITBLUEPRINT_OBJECT_EVENT_PREFIX + '.Rank.Updated',
                                {'id': id, 'rank': rank})
 
-    def onSchedulingUnitBlueprintPlacedUpdated(self, payload=None):
-        payload_dict = json.loads(payload)
-        id = payload_dict['id']
-        placed = payload_dict['new']['placed']
-        self._sendNotification(TMSS_SCHEDULINGUNITBLUEPRINT_OBJECT_EVENT_PREFIX + '.Placed.Updated',
-                               {'id': id, 'placed': placed})
-
     def onSchedulingUnitBlueprintPriorityQueueUpdated(self, payload=None):
         payload_dict = json.loads(payload)
         id = payload_dict['id']
diff --git a/SAS/TMSS/backend/services/scheduling/lib/constraints.py b/SAS/TMSS/backend/services/scheduling/lib/constraints.py
index 9dfa61ead1e..49a4241bdd5 100644
--- a/SAS/TMSS/backend/services/scheduling/lib/constraints.py
+++ b/SAS/TMSS/backend/services/scheduling/lib/constraints.py
@@ -2234,9 +2234,6 @@ def get_blocking_scheduled_units(scheduling_unit: models.SchedulingUnitBlueprint
 def get_blocking_scheduled_or_observing_units(scheduling_unit: models.SchedulingUnitBlueprint, proposed_start_time: datetime=None) -> QuerySet:
     return get_blocking_units(scheduling_unit, proposed_start_time, (models.SchedulingUnitStatus.Choices.SCHEDULED.value, models.SchedulingUnitStatus.Choices.OBSERVING.value))
 
-def get_blocking_placed_units(scheduling_unit: models.SchedulingUnitBlueprint, proposed_start_time: datetime=None) -> QuerySet:
-    return get_blocking_units(scheduling_unit, proposed_start_time, (models.SchedulingUnitStatus.Choices.SCHEDULABLE.value,)).filter(placed=True)
-
 def get_blocking_units(scheduling_unit: models.SchedulingUnitBlueprint, proposed_start_time: datetime = None, blocking_statuses: Iterable[str]=None) -> QuerySet:
     from .dynamic_scheduling import DEFAULT_INTER_OBSERVATION_GAP
     units = models.SchedulingUnitBlueprint.objects.filter(obsolete_since__isnull=True)
diff --git a/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py b/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py
index 250ca852f94..6deb9699d5e 100644
--- a/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py
+++ b/SAS/TMSS/backend/services/scheduling/lib/dynamic_scheduling.py
@@ -74,9 +74,6 @@ from operator import attrgetter
 # LOFAR needs to have a gap in between observations to (re)initialize hardware.
 DEFAULT_NEXT_STARTTIME_GAP = timedelta(seconds=180)
 from lofar.sas.tmss.tmss.tmssapp.subtasks import DEFAULT_INTER_OBSERVATION_GAP
-# how far ahead do we schedule? (beyond this limit we just place the units without scheduling them).
-DEFAULT_SCHEDULING_WINDOW_WIDTH = timedelta(days=3)
-
 
 class Scheduler:
     """
@@ -93,13 +90,12 @@ class Scheduler:
       - schedule the best next dynamic scheduling unit
       - position the rest of the dynamic scheduling units at their most likely timestamp where they will be observed.
     """
-    def __init__(self, scheduling_window_width: timedelta=DEFAULT_SCHEDULING_WINDOW_WIDTH) -> None:
+    def __init__(self) -> None:
         self._scheduling_thread = None
         self._scheduling_thread_running = False
         self._do_schedule_event = Event()
         self.search_gridder = Gridder(grid_minutes=1*60)
         self.fine_gridder = Gridder(grid_minutes=1)
-        self.scheduling_window_width = scheduling_window_width
         super().__init__()
         # make sure initial status is idle
         models.Subsystem.Activator('scheduler').deactivate()
@@ -237,7 +233,7 @@ class Scheduler:
                     try:
                         # first put the unit at it's requested 'at' timestamp.
                         at_timestamp = get_at_constraint_timestamp(schedulable_unit)
-                        set_scheduling_unit_blueprint_start_times(schedulable_unit, first_start_time=at_timestamp, placed=True)
+                        set_scheduling_unit_blueprint_start_times(schedulable_unit, first_start_time=at_timestamp)
 
                         if not can_run_at(schedulable_unit, at_timestamp, self.fine_gridder):
                             unschedulable_unit = determine_unschedulable_reason_and_mark_unschedulable_if_needed(schedulable_unit, at_timestamp, at_timestamp + schedulable_unit.specified_observation_duration,
@@ -252,7 +248,7 @@ class Scheduler:
                             logger.info("Scheduled fixed_time unit [%s/%s] id=%d at '%s'", i, len(schedulable_units), schedulable_unit.id, at_timestamp)
                             scheduled_units.append(scheduled_unit)
 
-                            scheduled_B_units = self.place_or_schedule_B_priority_units_in_gaps_around_scheduling_unit(scheduled_unit, do_schedule=True)
+                            scheduled_B_units = self.schedule_B_priority_units_in_gaps_around_scheduling_unit(scheduled_unit)
                             scheduled_units.extend(scheduled_B_units)
                         else:
                             unschedulable_unit = determine_unschedulable_reason_and_mark_unschedulable_if_needed(schedulable_unit, at_timestamp, at_timestamp + schedulable_unit.specified_observation_duration,
@@ -302,7 +298,7 @@ class Scheduler:
         with models.Subsystem.Activator('scheduler'):
             logger.info("Updating (dynamic) schedule....")
 
-            placed_or_scheduled_units = []
+            scheduled_units = []
 
             lower_bound = round_to_second_precision(datetime.utcnow() + DEFAULT_NEXT_STARTTIME_GAP)
             upper_bound = lower_bound + timedelta(days=1)
@@ -312,37 +308,27 @@ class Scheduler:
 
             while lower_bound < upper_bound:
                 self._raise_if_triggered()
-                # scheduled units in the near upcoming window of [now, now+scheduling_window_width]
-                # 'place' units in the rest of the cycle
-                # scheduled-vs-placed rationale:
-                #   1) scheduling/unscheduling takes time and creates/prepares/deletes dataproducts etc.
-                #   2) users "expect" a scheduled unit to be executed at that starttime. Unscheduling moving and scheduling again, confuses them.
-                #   3) user "accept" non-scheduled-but-placed units to be moved around if needed
-                do_schedule = lower_bound - datetime.utcnow() < self.scheduling_window_width
-
-                # find and schedule/place the next best unit
-                # ignore/exclude as candidates the unit(s) which are already placed/scheduled in this round.
-                # when new candidates are overlapping with already placed/scheduled units, they are re-evaluated to see who wins.
-                to_be_excluded_units = set(placed_or_scheduled_units) - set(get_dynamically_schedulable_scheduling_units().filter(placed=False).all())
-                placed_or_scheduled_unit = self.place_or_schedule_next_scheduling_unit(lower_bound,
-                                                                                       lower_bound + timedelta(hours=24),
-                                                                                       do_schedule=do_schedule,
-                                                                                       exclude_units=to_be_excluded_units)
-
-                if placed_or_scheduled_unit:
-                    placed_or_scheduled_units.append(placed_or_scheduled_unit)
+
+                # find and schedule the next best unit
+                # ignore/exclude as candidates the unit(s) which are already scheduled in this round.
+                # when new candidates are overlapping with already scheduled units, they are re-evaluated to see who wins.
+                to_be_excluded_units = set(scheduled_units) - set(get_dynamically_schedulable_scheduling_units().all())
+                scheduled_unit = self.schedule_next_scheduling_unit(lower_bound,
+                                                                    lower_bound + timedelta(hours=24),
+                                                                    exclude_units=to_be_excluded_units)
+
+                if scheduled_unit:
+                    scheduled_units.append(scheduled_unit)
 
                     # see if we can fit any B-prio units in the new gap(s) in the schedule?
-                    placed_or_scheduled_B_units = self.place_or_schedule_B_priority_units_in_gaps_around_scheduling_unit(placed_or_scheduled_unit, do_schedule=do_schedule)
-                    placed_or_scheduled_units.extend(placed_or_scheduled_B_units)
+                    scheduled_B_units = self.schedule_B_priority_units_in_gaps_around_scheduling_unit(scheduled_unit)
+                    scheduled_units.extend(scheduled_B_units)
                 else:
-                    # nothing was placed/scheduled in the window lower_bound+24h, so advance and see if anything fits
+                    # nothing was scheduled in the window lower_bound+24h, so advance and see if anything fits
                     # check for any overlapping units...
                     blocking_units = models.SchedulingUnitBlueprint.objects.filter(
                         obsolete_since__isnull=True).filter(
-                        placed__isnull=True).filter(
-                        status__value__in=(models.SchedulingUnitStatus.Choices.SCHEDULABLE.value,
-                                           models.SchedulingUnitStatus.Choices.SCHEDULED.value,
+                        status__value__in=(models.SchedulingUnitStatus.Choices.SCHEDULED.value,
                                            models.SchedulingUnitStatus.Choices.OBSERVING.value)).filter(
                         on_sky_stop_time__gte=lower_bound).filter(
                         on_sky_stop_time__lte=upper_bound)
@@ -364,17 +350,17 @@ class Scheduler:
 
             logger.info("Finished updating dynamic schedule")
 
-            # loop over all remaining non-placed schedulable units, and make them unschedulable for the big search window (if they are unschedulable)
+            # loop over all remaining non-scheduled schedulable units, and make them unschedulable for the big search window (if they are unschedulable)
             # so they are ignored next time.
             # It's up to the user/operator to tweak their constraints which makes them schedulable again, for a next try.
-            for su in get_dynamically_schedulable_scheduling_units().filter(placed=False).all():
+            for su in get_dynamically_schedulable_scheduling_units().all():
                 determine_unschedulable_reason_and_mark_unschedulable_if_needed(su, datetime.utcnow(),
                                                                                 su.latest_possible_cycle_start_time,
                                                                                 proposed_start_time=None,
                                                                                 gridder=self.search_gridder,
                                                                                 raise_if_interruped=self._raise_if_triggered)
 
-            return placed_or_scheduled_units
+            return scheduled_units
 
 
     def find_best_next_schedulable_unit(self, scheduling_units:[models.SchedulingUnitBlueprint], lower_bound_start_time: datetime, upper_bound_stop_time: datetime) -> ScoredSchedulingUnit:
@@ -555,12 +541,10 @@ class Scheduler:
         return None
 
 
-    def place_or_schedule_next_scheduling_unit(self, lower_bound: datetime=None, upper_bound: datetime=None, do_schedule: bool=False, exclude_units: []=None) -> models.SchedulingUnitBlueprint:
-        '''find the best next upcoming schedulable scheduling unit and try to place/schedule it.
-        Overlapping existing scheduled units are unplaced/unscheduled if their score is lower.
-        :return: the placed/scheduled scheduling unit.'''
-
-        log_prefix = 'schedule_next_scheduling_unit' if do_schedule else 'place_or_schedule_next_scheduling_unit'
+    def schedule_next_scheduling_unit(self, lower_bound: datetime=None, upper_bound: datetime=None, exclude_units: []=None) -> models.SchedulingUnitBlueprint:
+        '''find the best next upcoming schedulable scheduling unit and try to schedule it.
+        Overlapping existing scheduled units are unscheduled if their score is lower.
+        :return: the scheduled scheduling unit.'''
 
         queue_A = models.PriorityQueueType.objects.get(value=models.PriorityQueueType.Choices.A.value)
         queue_B = models.PriorityQueueType.objects.get(value=models.PriorityQueueType.Choices.B.value)
@@ -594,13 +578,13 @@ class Scheduler:
             if len(candidate_units) == 0:
                 continue  # continue with the next in order schedulable_units
 
-            logger.info("%s: %s candidate_units: %s", log_prefix, len(candidate_units), ','.join([str(su.id) for su in sorted(candidate_units, key=lambda x: x.id)]) or 'None')
+            logger.info("schedule_next_scheduling_unit: %s candidate_units: %s", len(candidate_units), ','.join([str(su.id) for su in sorted(candidate_units, key=lambda x: x.id)]) or 'None')
 
             # search in a forward sliding window for the best scheduling_unit that can be scheduled
             if lower_bound is None:
                 lower_bound = datetime.utcnow() + DEFAULT_NEXT_STARTTIME_GAP
             if upper_bound is None:
-                upper_bound = lower_bound + self.scheduling_window_width
+                upper_bound = lower_bound + timedelta(hours=24)
             lower_bound_start_time = round_to_second_precision(lower_bound)
             upper_bound_stop_time = round_to_second_precision(upper_bound)
 
@@ -620,7 +604,7 @@ class Scheduler:
                     window_upper_bound_stop_time  = round_to_second_precision(window_upper_bound_stop_time)
 
                     # try to find the best next scheduling_unit
-                    logger.info("%s: searching for best scheduling unit to schedule in window ['%s', '%s']", log_prefix, window_lower_bound_start_time, window_upper_bound_stop_time)
+                    logger.info("schedule_next_scheduling_unit: searching for best scheduling unit to schedule in window ['%s', '%s']", window_lower_bound_start_time, window_upper_bound_stop_time)
                     best_scored_scheduling_unit = self.find_best_next_schedulable_unit(candidate_units,
                                                                                        window_lower_bound_start_time,
                                                                                        window_upper_bound_stop_time)
@@ -636,29 +620,26 @@ class Scheduler:
                         # make start_time "look nice" for us humans
                         best_start_time = round_to_second_precision(best_start_time)
 
-                        logger.info("%s: found best candidate id=%s '%s' weighted_score=%.3f start_time=%s interrupts_telescope=%s",
-                                    log_prefix, best_scheduling_unit.id, best_scheduling_unit.name, best_scheduling_unit_score, best_start_time, best_scheduling_unit.interrupts_telescope)
+                        logger.info("schedule_next_scheduling_unit: found best candidate id=%s '%s' weighted_score=%.3f start_time=%s interrupts_telescope=%s",
+                                    best_scheduling_unit.id, best_scheduling_unit.name, best_scheduling_unit_score, best_start_time, best_scheduling_unit.interrupts_telescope)
 
-                        if do_schedule:
-                            placed_or_scheduled_unit = self.try_schedule_unit(best_scheduling_unit, best_start_time)
-                        else:
-                            placed_or_scheduled_unit = self.try_place_unit(best_scheduling_unit, best_start_time)
+                        scheduled_unit = self.try_schedule_unit(best_scheduling_unit, best_start_time)
 
-                        if placed_or_scheduled_unit is None:
-                            # we had a best_scored_scheduling_unit, but it could not be placed/scheduled here.
+                        if scheduled_unit is None:
+                            # we had a best_scored_scheduling_unit, but it could not be scheduled here.
                             # remove it from the candidates, and do not evaluate it again in this window.
                             candidate_units.remove(best_scheduling_unit)
                             continue
                         else:
                             self.log_schedule(log_level=logging.INFO)
-                            # done, just return the placed/scheduled_unit
-                            return placed_or_scheduled_unit
+                            # done, just return the scheduled_unit
+                            return scheduled_unit
 
                     else:
-                        logger.info("%s: no scheduling unit found which could be scheduled in window ['%s', '%s']", log_prefix, window_lower_bound_start_time, window_upper_bound_stop_time)
+                        logger.info("schedule_next_scheduling_unit: no scheduling unit found which could be scheduled in window ['%s', '%s']", window_lower_bound_start_time, window_upper_bound_stop_time)
 
                 except SubtaskSchedulingException as e:
-                    logger.error("%s: Could not schedule scheduling_unit id=%s name='%s'. Error: %s", log_prefix, best_scheduling_unit.id, best_scheduling_unit.name, e)
+                    logger.error("%s: Could not schedule scheduling_unit id=%s name='%s'. Error: %s", best_scheduling_unit.id, best_scheduling_unit.name, e)
 
                     # prevent that it keeps trying to schedule this failed unit in this scheduler-round
                     candidate_units.remove(best_scheduling_unit)
@@ -671,14 +652,14 @@ class Scheduler:
                         return None
                     else:
                         if can_run_after(best_scheduling_unit, best_start_time, self.search_gridder) and not best_scheduling_unit.interrupts_telescope:
-                            logger.info("%s: Unschedulable scheduling_unit id=%s can run later than '%s'. Marking it as schedulable again...", log_prefix, best_scheduling_unit.id, best_start_time)
+                            logger.info("schedule_next_scheduling_unit: Unschedulable scheduling_unit id=%s can run later than '%s'. Marking it as schedulable again...", best_scheduling_unit.id, best_start_time)
                             # yep, can run later, so mark it as schedulable again, and let it be handled in a new scheduler-round
                             mark_independent_subtasks_in_scheduling_unit_blueprint_as_schedulable(best_scheduling_unit)
                         else:
                             mark_independent_subtasks_in_scheduling_unit_blueprint_as_unschedulable(best_scheduling_unit, str(e))
 
                 if not candidate_units:
-                    logger.debug("%s: no more candidate units...", log_prefix)
+                    logger.debug("%s: no more candidate units...")
                     return None
 
                 # advance the window at the upper side only so more candidates fit in
@@ -729,72 +710,22 @@ class Scheduler:
 
             return scheduled_scheduling_unit
 
-    def try_place_unit(self, scheduling_unit: models.SchedulingUnitBlueprint, start_time: datetime) -> models.SchedulingUnitBlueprint:
-        '''Try placing the unit at the given start_time
-        un-places any overlapping placed units'''
-        logger.info("try_place_unit id=%s queue=%s '%s' start_time=%s", scheduling_unit.id, scheduling_unit.priority_queue.value, scheduling_unit.name, start_time)
-
-        # check if the given unit was already placed at the given time.
-        scheduling_unit_from_db = models.SchedulingUnitBlueprint.objects.get(id=scheduling_unit.id)
-        if scheduling_unit_from_db.placed:
-            if abs((scheduling_unit_from_db.scheduled_start_time - start_time).total_seconds() < 1):
-                logger.info("scheduling_unit id=%s '%s' was already placed at start_time='%s', skipping...", scheduling_unit.id, scheduling_unit.name, start_time)
-                return None
-
-        # are there any blocking scheduled or observing scheduling_units still in the way?
-        blocking_scheduling_units = get_blocking_scheduled_or_observing_units(scheduling_unit, start_time)
-
-        if blocking_scheduling_units.exists():
-            logger.warning("cannot place scheduling_unit id=%s '%s' at start_time=%s because there are %d other units blocking it",
-                scheduling_unit.id, scheduling_unit.name, start_time, blocking_scheduling_units.count())
-            update_subtasks_start_times_for_scheduling_unit(scheduling_unit, start_time, placed=False)
-            return None
-
-        # un-place any previously overlapping/blocking placed unit with a lower score
-        blocking_placed_units = list(get_blocking_placed_units(scheduling_unit, start_time))
-        if blocking_placed_units:
-            # compute weighted scores for all blocking placed- and the candidate scheduling units
-            # because the weighted scores are also normalized over the given list of units.
-            # So, to make a fair comparison if the candidate is 'better', it has to be scored in the same set.
-            to_be_rescored_units = [SchedulingUnitAndStartTime(u, u.scheduled_start_time) for u in blocking_placed_units] + [SchedulingUnitAndStartTime(scheduling_unit, start_time)]
-            logger.info("re-scoring units %s", ','.join(str(s.scheduling_unit.id) for s in to_be_rescored_units))
-            scored_scheduling_units = compute_scores_for_units_with_start_time(to_be_rescored_units,
-                                                                               min([x.start_time for x in to_be_rescored_units]),
-                                                                               max([x.start_time+x.scheduling_unit.specified_observation_duration for x in to_be_rescored_units]),
-                                                                               gridder=self.fine_gridder)
-
-            rescored_unit = next((ssu for ssu in scored_scheduling_units if ssu.scheduling_unit.id==scheduling_unit.id), None)
-
-            if rescored_unit:
-                lower_scoring_units = [ssu for ssu in scored_scheduling_units if ssu.weighted_score < rescored_unit.weighted_score]
-                for blocking_placed_unit in lower_scoring_units:
-                    blocking_placed_unit.scheduling_unit.placed = False
-                    blocking_placed_unit.scheduling_unit.save()
-
-        # we tried to make room for our candidate
-        # place it if no more blocking other units, else just assign the new starttime
-        if get_blocking_placed_units(scheduling_unit, start_time).exists():
-            update_subtasks_start_times_for_scheduling_unit(scheduling_unit, start_time, placed=False)
-            return None
 
-        return update_subtasks_start_times_for_scheduling_unit(scheduling_unit, start_time, placed=True)
-
-
-    def place_or_schedule_B_priority_units_in_gaps_around_scheduling_unit(self, scheduling_unit: models.SchedulingUnitBlueprint, do_schedule: bool=False) -> [models.SchedulingUnitBlueprint]:
-        '''try to place/schedule one or more scheduling units from queue B in the gap between the given scheduled_unit and its previous observed+ unit'''
-        placed_units = []
+    def schedule_B_priority_units_in_gaps_around_scheduling_unit(self, scheduling_unit: models.SchedulingUnitBlueprint) -> [models.SchedulingUnitBlueprint]:
+        '''try to schedule one or more scheduling units from queue B in the gap between the given scheduled_unit and its previous observed+ unit'''
+        scheduled_units = []
 
         if not self.dynamic_scheduling_enabled:
-            logger.debug("skipping place_B_priority_units_in_gaps because dynamic_scheduling is not enabled")
-            return placed_units
+            logger.debug("skipping schedule_B_priority_units_in_gaps because dynamic_scheduling is not enabled")
+            return scheduled_units
 
-        logger.debug("place_B_priority_units_in_gaps: looking for B-queue units to be placed in gap(s) around unit id=%s", scheduling_unit.id)
+        logger.debug("schedule_B_priority_units_in_gaps: looking for B-queue units to be scheduled in gap(s) around unit id=%s", scheduling_unit.id)
 
-        schedulable_units_queue_B = get_dynamically_schedulable_scheduling_units(priority_queue=models.PriorityQueueType.objects.get(value=models.PriorityQueueType.Choices.B.value)).exclude(id=scheduling_unit.id).filter(placed=False)
+        schedulable_units_queue_B = get_dynamically_schedulable_scheduling_units(priority_queue=models.PriorityQueueType.objects.get(value=models.PriorityQueueType.Choices.B.value)).exclude(id=scheduling_unit.id)
         if not schedulable_units_queue_B.exists():
-            return placed_units
+            return scheduled_units
 
-        gaps = get_gaps_to_previous_and_next_observations_in_scheduling_unit(scheduling_unit, include_schedulable_unschedulable=not do_schedule)
+        gaps = get_gaps_to_previous_and_next_observations_in_scheduling_unit(scheduling_unit, include_schedulable_unschedulable=False)
         gaps = [gap for gap in gaps if gap[1] != datetime.max] # skip open-ended future gaps
 
         for gap in gaps:
@@ -805,7 +736,7 @@ class Scheduler:
             if (upper_bound_stop_time - lower_bound_start_time) <= 2*DEFAULT_INTER_OBSERVATION_GAP + timedelta(minutes=1):
                 continue # gap is too small
 
-            logger.info("place_B_priority_units_in_gaps: evaluating %s B-queue units in %d[min]-wide gap ['%s', '%s') %s unit id=%s",
+            logger.info("schedule_B_priority_units_in_gaps: evaluating %s B-queue units in %d[min]-wide gap ['%s', '%s') %s unit id=%s",
                         schedulable_units_queue_B.count(),
                         (upper_bound_stop_time-lower_bound_start_time).total_seconds()/60,
                         lower_bound_start_time, upper_bound_stop_time,
@@ -815,40 +746,34 @@ class Scheduler:
             best_B_candidate_for_gap = self.find_best_next_schedulable_unit(schedulable_units_queue_B, lower_bound_start_time=lower_bound_start_time, upper_bound_stop_time=upper_bound_stop_time)
             if best_B_candidate_for_gap is not None and best_B_candidate_for_gap.scheduling_unit is not None:
                 try:
-                    logger.info("place_B_priority_units_in_gaps: trying to %s B-queue unit id=%s at start_time='%s' in gap ['%s', '%s')",
-                                'schedule' if do_schedule else 'place',
+                    logger.info("schedule_B_priority_units_in_gaps: trying to schedule B-queue unit id=%s at start_time='%s' in gap ['%s', '%s')",
                                 best_B_candidate_for_gap.scheduling_unit.id, best_B_candidate_for_gap.start_time, lower_bound_start_time, upper_bound_stop_time)
 
-                    if do_schedule:
-                        maybe_scheduled_unit = self.try_schedule_unit(best_B_candidate_for_gap.scheduling_unit, best_B_candidate_for_gap.start_time)
-                        if maybe_scheduled_unit is not None and maybe_scheduled_unit.status.value==models.SchedulingUnitStatus.Choices.SCHEDULED.value:
-                            placed_units.append(best_B_candidate_for_gap.scheduling_unit)
-                    else:
-                        maybe_placed_unit = update_subtasks_start_times_for_scheduling_unit(best_B_candidate_for_gap.scheduling_unit, best_B_candidate_for_gap.start_time, placed=True)
-                        if maybe_placed_unit is not None and maybe_placed_unit.placed:
-                            placed_units.append(best_B_candidate_for_gap.scheduling_unit)
+                    maybe_scheduled_unit = self.try_schedule_unit(best_B_candidate_for_gap.scheduling_unit, best_B_candidate_for_gap.start_time)
+                    if maybe_scheduled_unit is not None and maybe_scheduled_unit.status.value==models.SchedulingUnitStatus.Choices.SCHEDULED.value:
+                        scheduled_units.append(best_B_candidate_for_gap.scheduling_unit)
                 except Exception as e:
-                    logger.exception("place_B_priority_units_in_gaps: Could not schedule/place B-queue unit id=%s in gap( ['%s', '%s'). %s",
+                    logger.exception("schedule_B_priority_units_in_gaps: Could not schedule B-queue unit id=%s in gap( ['%s', '%s'). %s",
                                      best_B_candidate_for_gap.scheduling_unit.id, lower_bound_start_time, upper_bound_stop_time, str(e))
 
                 try:
-                    if best_B_candidate_for_gap.scheduling_unit in placed_units:
-                        # unit has successfully been scheduled/placed.
+                    if best_B_candidate_for_gap.scheduling_unit in scheduled_units:
+                        # unit has successfully been scheduled.
                         # Recurse. There may be a new gap, so let's try to squeeze in more.
                         best_B_candidate_for_gap.scheduling_unit.refresh_from_db()
-                        logger.debug("place_B_priority_units_in_gaps: recursing to place more B-queue units next to just placed unit id=%s", best_B_candidate_for_gap.scheduling_unit.id)
-                        placed_units.extend(self.place_or_schedule_B_priority_units_in_gaps_around_scheduling_unit(best_B_candidate_for_gap.scheduling_unit, do_schedule=do_schedule))
+                        logger.debug("schedule_B_priority_units_in_gaps: recursing to schedule more B-queue units next to just scheduled unit id=%s", best_B_candidate_for_gap.scheduling_unit.id)
+                        scheduled_units.extend(self.schedule_B_priority_units_in_gaps_around_scheduling_unit(best_B_candidate_for_gap.scheduling_unit))
                 except RecursionError as e:
                     logger.error("Max recursion depth reached. Skipping further scheduling of B-queue units in %d[min]-wide gap( ['%s', '%s')", (upper_bound_stop_time-lower_bound_start_time).total_seconds()/60, lower_bound_start_time, upper_bound_stop_time)
             else:
-                logger.info("place_B_priority_units_in_gaps: could not find any B-queue unit out of the %d candidates which fits in %dmin gap ['%s', '%s') next to unit id=%s",
+                logger.info("schedule_B_priority_units_in_gaps: could not find any B-queue unit out of the %d candidates which fits in %dmin gap ['%s', '%s') next to unit id=%s",
                             schedulable_units_queue_B.count(),
                             (upper_bound_stop_time-lower_bound_start_time).total_seconds()/60.0,
                             lower_bound_start_time,
                             upper_bound_stop_time,
                             scheduling_unit.id)
 
-        return placed_units
+        return scheduled_units
 
 
     def log_schedule(self, log_level: int=logging.INFO):
@@ -872,8 +797,8 @@ class Scheduler:
 
                 for unit in units_in_schedule:
                     try:
-                        if unit.status.value==models.SchedulingUnitStatus.Choices.SCHEDULABLE.value and not unit.placed:
-                            # skip non-placed schedulable units
+                        if unit.status.value==models.SchedulingUnitStatus.Choices.SCHEDULABLE.value:
+                            # skip schedulable units
                             continue
 
                         task_center_time, transit_time, offset_to_transit, lowest_elevation, elevation_at_center, elevation_at_transit = get_timestamps_elevations_and_offset_to_transit(unit, unit.scheduled_start_time)
@@ -1026,11 +951,11 @@ class TMSSDynamicSchedulingMessageHandler(TMSSEventMessageHandler):
                 logger.info("triggering scheduler for fixed_time unit id=%s status=%s", id, status)
                 self.scheduler.trigger()
         elif scheduling_unit.is_dynamically_scheduled and self.scheduler.dynamic_scheduling_enabled and not self.scheduler.is_scheduling:
-            if (status in [models.SchedulingUnitStatus.Choices.SCHEDULABLE.value] and not scheduling_unit.placed) or \
-                status in [models.SchedulingUnitStatus.Choices.OBSERVING.value,
+            if status in [models.SchedulingUnitStatus.Choices.SCHEDULABLE.value,
+                           models.SchedulingUnitStatus.Choices.OBSERVING.value,
                            models.SchedulingUnitStatus.Choices.CANCELLED.value,
                            models.SchedulingUnitStatus.Choices.OBSERVED.value]:
-                logger.info("triggering scheduler for dynamic unit id=%s status=%s placed=%s", id, status, scheduling_unit.placed)
+                logger.info("triggering scheduler for dynamic unit id=%s status=%s", id, status)
                 self.scheduler.trigger()
 
 
@@ -1040,7 +965,7 @@ class TMSSDynamicSchedulingMessageHandler(TMSSEventMessageHandler):
             scheduling_unit_blueprint = models.SchedulingUnitBlueprint.objects.get(id=id)
             at = get_at_constraint_timestamp(scheduling_unit_blueprint)
             if at is not None:
-                update_subtasks_start_times_for_scheduling_unit(scheduling_unit_blueprint, at, placed=False)
+                update_subtasks_start_times_for_scheduling_unit(scheduling_unit_blueprint, at)
         except:
             pass
         self.onSchedulingUnitBlueprintConstraintsRankOrQueueUpdated(id)
@@ -1062,7 +987,7 @@ class TMSSDynamicSchedulingMessageHandler(TMSSEventMessageHandler):
         elif scheduling_unit_blueprint.status.value == models.SchedulingUnitStatus.Choices.SCHEDULED.value:
             logger.info("constraints/queue/priority for scheduled scheduling unit id=%s changed: unscheduling it, which will triggering a dynamic scheduling update...", id)
             unschedule_subtasks_in_scheduling_unit_blueprint(scheduling_unit_blueprint)
-        elif scheduling_unit_blueprint.status.value == models.SchedulingUnitStatus.Choices.SCHEDULABLE.value and not scheduling_unit_blueprint.placed:
+        elif scheduling_unit_blueprint.status.value == models.SchedulingUnitStatus.Choices.SCHEDULABLE.value and not self.scheduler.is_scheduling:
             logger.info("constraints/queue/priority for schedulable scheduling unit id=%s changed: triggering a dynamic scheduling update...", id)
             self.scheduler.trigger()
 
@@ -1381,7 +1306,7 @@ def unschededule_blocking_scheduled_units_if_needed_and_possible(candidate_sched
                 logger.info("unscheduling id=%s '%s' because it is in the way and has a lower trigger_priority=%s than the best candidate id=%s '%s' trigger_priority=%s start_time=%s",
                             scheduled_scheduling_unit.id, scheduled_scheduling_unit.name, scheduled_scheduling_unit.project.trigger_priority,
                             candidate_scheduling_unit.id, candidate_scheduling_unit.name, candidate_scheduling_unit.project.trigger_priority, candidate_scheduling_unit.scheduled_start_time)
-                unschedule_subtasks_in_scheduling_unit_blueprint(scheduled_scheduling_unit, placed=False)
+                unschedule_subtasks_in_scheduling_unit_blueprint(scheduled_scheduling_unit)
                 if not can_run_after(scheduled_scheduling_unit, scheduled_scheduling_unit.scheduled_start_time, gridder):
                     logger.info("marking id=%s '%s' unschedulable because it cannot run after start_time=%s",
                                 scheduled_scheduling_unit.id, scheduled_scheduling_unit.name, scheduled_scheduling_unit.scheduled_start_time)
@@ -1396,7 +1321,7 @@ def unschededule_blocking_scheduled_units_if_needed_and_possible(candidate_sched
                             scheduled_scheduling_unit.id, scheduled_scheduling_unit.name, scheduled_scheduling_unit.priority_queue.value,
                             candidate_scheduling_unit.id, candidate_scheduling_unit.name, candidate_scheduling_unit.priority_queue.value,
                             candidate_scheduling_unit.scheduled_start_time)
-                unschedule_subtasks_in_scheduling_unit_blueprint(scheduled_scheduling_unit, placed=False)
+                unschedule_subtasks_in_scheduling_unit_blueprint(scheduled_scheduling_unit)
         # compare the re-scored weighted_scores
         elif rescored_candidate.weighted_score > rescored_scheduled_unit.weighted_score:
             # the candidate clearly wins, let's try to make some space by unscheduling the scheduled_scheduling_unit
@@ -1404,7 +1329,7 @@ def unschededule_blocking_scheduled_units_if_needed_and_possible(candidate_sched
                         scheduled_scheduling_unit.id, scheduled_scheduling_unit.name,
                         candidate_scheduling_unit.id, candidate_scheduling_unit.name,
                         candidate_scheduling_unit.scheduled_start_time)
-            unschedule_subtasks_in_scheduling_unit_blueprint(scheduled_scheduling_unit, placed=False)
+            unschedule_subtasks_in_scheduling_unit_blueprint(scheduled_scheduling_unit)
         elif enough_stations_available_for_scheduling_unit(scheduled_scheduling_unit, unavailable_stations=candidate_scheduling_unit.main_observation_specified_stations):
             # the candidate does not win, but it's ok to unschedule the scheduled_scheduling_unit because it can run without the candidate's stations.
             logger.info("rescheduling id=%s '%s' start_time=%s without the stations of the best candidate id=%s '%s' start_time=%s",
diff --git a/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py b/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py
index 2d82c672d2e..140960fa1b1 100755
--- a/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py
+++ b/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py
@@ -90,7 +90,7 @@ class BaseDynamicSchedulingTestCase(unittest.TestCase):
         cls.scheduling_set_medium = models.SchedulingSet.objects.create(**SchedulingSet_test_data(project=cls.project_medium))
         cls.scheduling_set_high = models.SchedulingSet.objects.create(**SchedulingSet_test_data(project=cls.project_high))
 
-        cls.scheduler = Scheduler(scheduling_window_width=timedelta(hours=48))
+        cls.scheduler = Scheduler()
         cls.message_handler = TMSSDynamicSchedulingMessageHandler()
 
     def setUp(self) -> None:
@@ -653,7 +653,7 @@ class TestFixedTimeScheduling(BaseDynamicSchedulingTestCase):
         scheduling_unit_blueprint.save()
         wipe_evaluate_constraints_caches()
         mark_independent_subtasks_in_scheduling_unit_blueprint_as_schedulable(scheduling_unit_blueprint)
-        self.scheduler.place_or_schedule_next_scheduling_unit(do_schedule=True)
+        self.scheduler.schedule_next_scheduling_unit()
         scheduling_unit_blueprint.refresh_from_db()
         self.assertEqual(models.SchedulingUnitStatus.Choices.SCHEDULED.value, scheduling_unit_blueprint.status.value)
 
@@ -1406,9 +1406,9 @@ class TestFixedTimeScheduling(BaseDynamicSchedulingTestCase):
 
 
 
-    def test_bug_fix_TMSS_2538_B_prio_unit_should_be_placed_and_scheduled_in_gaps_only(self):
+    def test_bug_fix_TMSS_2538_B_prio_unit_should_be_scheduled_in_gaps_only(self):
         """
-        Test to reproduce and fix the reported bug that a B-prio unit is placed/scheduled overlapping an A-prio unit.
+        Test to reproduce and fix the reported bug that a B-prio unit is scheduled overlapping an A-prio unit.
         See: https://support.astron.nl/jira/browse/TMSS-2538
         """
         # schedule a normal 4hour-long unit at 1hour into the future
@@ -1453,15 +1453,14 @@ class TestFixedTimeScheduling(BaseDynamicSchedulingTestCase):
         self.assertEqual('dynamic', scheduling_unit_blueprint_B.scheduling_constraints_doc['scheduler'])
         self.assertEqual(scheduling_unit_blueprint_B.status.value, models.SchedulingUnitStatus.Choices.SCHEDULABLE.value)
 
-        # try to place the B unit...
-        self.scheduler.place_or_schedule_next_scheduling_unit(lower_bound=round_to_second_precision(datetime.utcnow()), do_schedule=False)
+        # try to schedule the B unit...
+        self.scheduler.schedule_next_scheduling_unit(lower_bound=round_to_second_precision(datetime.utcnow()))
 
-        # Assert the scheduling_unit has been placed and is schedulable.
+        # Assert the scheduling_unit has been scheduled.
         scheduling_unit_blueprint_B.refresh_from_db()
-        self.assertEqual(models.SchedulingUnitStatus.Choices.SCHEDULABLE.value, scheduling_unit_blueprint_B.status.value)
-        self.assertTrue(scheduling_unit_blueprint_B.placed)
+        self.assertEqual(models.SchedulingUnitStatus.Choices.SCHEDULED.value, scheduling_unit_blueprint_B.status.value)
 
-        # check that the bug has been solved: unitB should NOT be overlapping with unitA, and be placed before it.
+        # check that the bug has been solved: unitB should NOT be overlapping with unitA, and be scheduled before it.
         self.assertFalse(scheduling_unit_blueprint_B.scheduled_start_time > scheduling_unit_blueprint_A.scheduled_start_time and
                          scheduling_unit_blueprint_B.scheduled_start_time < scheduling_unit_blueprint_A.scheduled_stop_time)
         self.assertTrue(scheduling_unit_blueprint_B.scheduled_stop_time < scheduling_unit_blueprint_A.scheduled_start_time)
@@ -1474,15 +1473,15 @@ class TestFixedTimeScheduling(BaseDynamicSchedulingTestCase):
         self.assertEqual(models.SchedulingUnitStatus.Choices.SCHEDULED.value, scheduling_unit_blueprint_A.status.value)
         self.assertEqual(at, scheduling_unit_blueprint_A.scheduled_start_time)
 
-        # check that the bug has been solved: unitB should NOT be overlapping with unitA, and be placed before it.
+        # check that the bug has been solved: unitB should NOT be overlapping with unitA, and be scheduled before it.
         self.assertFalse(scheduling_unit_blueprint_B.scheduled_start_time > scheduling_unit_blueprint_A.scheduled_start_time and
                          scheduling_unit_blueprint_B.scheduled_start_time < scheduling_unit_blueprint_A.scheduled_stop_time)
         self.assertTrue(scheduling_unit_blueprint_B.scheduled_stop_time < scheduling_unit_blueprint_A.scheduled_start_time)
 
 
-    def test_bug_fix_TMSS_2563_place_B_priority_units_incorrectly_placed(self):
+    def test_bug_fix_TMSS_2563_schedule_B_priority_units_incorrect_start_time(self):
         """
-        Test to reproduce and fix the reported bug that placing B-prio unit(s) in a gap between A-prio units sometimes yields to the B-unit being placed at the incorrect timestamp and blocking other B-units.
+        Test to reproduce and fix the reported bug that placing B-prio unit(s) in a gap between A-prio units sometimes yields to the B-unit being scheduled at the incorrect timestamp and blocking other B-units.
         See: https://support.astron.nl/jira/browse/TMSS-2563
         """
         # schedule two fixed-time 1hour-long units, starting in just 4min and in 2h
@@ -1861,7 +1860,7 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase):
             reservation.save()
 
             # # call the method-under-test.
-            scheduled_unit = self.scheduler.place_or_schedule_next_scheduling_unit(do_schedule=True)
+            scheduled_unit = self.scheduler.schedule_next_scheduling_unit()
 
             # if the reservation is exclusive for project_A, then unit A should be scheduled, and unit B unchedulable
             # and vice versa
@@ -2984,8 +2983,7 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase):
                     self.assertTrue(abs((best_scored_scheduling_unit.start_time - expected_start_time).total_seconds()) < 300)
 
                     placed_scheduling_unit = update_subtasks_start_times_for_scheduling_unit(best_scored_scheduling_unit.scheduling_unit,
-                                                                                             best_scored_scheduling_unit.start_time,
-                                                                                             placed=True)
+                                                                                             best_scored_scheduling_unit.start_time)
                     self.assertTrue(abs((placed_scheduling_unit.scheduled_start_time - expected_start_time).total_seconds()) < 300)
 
                     from datetime import date, time, datetime as dt
@@ -3290,7 +3288,7 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase):
         su_blueprint_1977 = create_scheduling_unit_blueprint_and_tasks_and_subtasks_from_scheduling_unit_draft(su_draft_1977)
 
         # schedule it dynamically to find a nice timestamp where it will be able to run
-        scheduled_unit = self.scheduler.place_or_schedule_next_scheduling_unit(do_schedule=True)
+        scheduled_unit = self.scheduler.schedule_next_scheduling_unit()
         self.assertIsNotNone(scheduled_unit)
         self.assertEqual(su_blueprint_1977.id, scheduled_unit.id)
 
@@ -3624,7 +3622,7 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase):
                                         start_time=datetime.utcnow()+timedelta(hours=1),
                                         stop_time =datetime.utcnow()+timedelta(hours=1, minutes=1))
 
-        # let the scheduler try to place/schedule the unit
+        # let the scheduler try to schedule the unit
         # it should not find a spot (because of the high min_elevation)
         self.scheduler.do_dynamic_schedule()
 
@@ -3787,7 +3785,7 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase):
             self.assertNotIn('CS032', su_other_scheduled.main_observation_used_stations)
             self.scheduler.log_schedule() # for reference
 
-    def test_bugfix_TMSS_2638_TMSS_2639__place_B_prio_units_in_gaps_with_and_without_overlapping_IDOLS_observation(self):
+    def test_bugfix_TMSS_2638_TMSS_2639_schedule_B_prio_units_in_gaps_with_and_without_overlapping_IDOLS_observation(self):
         '''See https://support.astron.nl/jira/browse/TMSS-2638
            and https://support.astron.nl/jira/browse/TMSS-2639
         '''
@@ -3848,18 +3846,14 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase):
         sub_B = create_scheduling_unit_blueprint_and_tasks_and_subtasks_from_scheduling_unit_draft(sud_B)
         self.assertEqual('B', sub_B.priority_queue.value)
 
-        # make sure the unit is not placed yet, so the scheduler will consider it as a candidate
-        sub_B.placed = False
-        sub_B.save()
-
         # and make sure we enable dynamic (B-prio) scheduling for this moment ...
         setting = models.Setting.objects.get(name=models.SystemSettingFlag.Choices.DYNAMIC_SCHEDULING_ENABLED.value)
         setting.value = True
         setting.save()
 
-        # try to schedule it via the place_B_priority_units_in_gaps_around_scheduling_unit for both sub_fixed_one and sub_fixed_two
+        # try to schedule it via the schedule_B_priority_units_in_gaps_around_scheduling_unit for both sub_fixed_one and sub_fixed_two
         for sub_fixed in (sub_fixed_one, sub_fixed_two):
-            scheduled_units = self.scheduler.place_or_schedule_B_priority_units_in_gaps_around_scheduling_unit(sub_fixed, do_schedule=True)
+            scheduled_units = self.scheduler.schedule_B_priority_units_in_gaps_around_scheduling_unit(sub_fixed)
             self.assertEqual(1, len(scheduled_units))
             self.assertTrue(sub_B in scheduled_units)
             sub_B.refresh_from_db()
@@ -3867,8 +3861,6 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase):
 
             # reset/unschedule it
             sub_B = unschedule_subtasks_in_scheduling_unit_blueprint(sub_B)
-            sub_B.placed = False
-            sub_B.save()
 
         # again, everything ok so far
         # now we have two scheduled fixed units with an expected gap in between.
@@ -3931,9 +3923,9 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase):
         setting.value = True
         setting.save()
 
-        # ... and try to schedule the B-prio unit again via the place_B_priority_units_in_gaps_around_scheduling_unit for both sub_fixed_one and sub_fixed_two
+        # ... and try to schedule the B-prio unit again via the schedule_B_priority_units_in_gaps_around_scheduling_unit for both sub_fixed_one and sub_fixed_two
         for sub_fixed in (sub_fixed_one, sub_fixed_two):
-            scheduled_units = self.scheduler.place_or_schedule_B_priority_units_in_gaps_around_scheduling_unit(sub_fixed, do_schedule=True)
+            scheduled_units = self.scheduler.schedule_B_priority_units_in_gaps_around_scheduling_unit(sub_fixed)
             self.assertEqual(1, len(scheduled_units))
             self.assertTrue(sub_B in scheduled_units)
             sub_B.refresh_from_db()
@@ -3941,11 +3933,9 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase):
 
             # reset/unschedule it
             sub_B = unschedule_subtasks_in_scheduling_unit_blueprint(sub_B)
-            sub_B.placed = False
-            sub_B.save()
 
-    def test_competing_units_are_placed_non_overlapping(self):
-        """ Have a set of similar survey-like units, and make sure they are scheduled/placed one after the other in a non-overlapping way"""
+    def test_competing_units_are_scheduled_non_overlapping(self):
+        """ Have a set of similar survey-like units, and make sure they are scheduled one after the other in a non-overlapping way"""
 
         # prepare the test with a fixed unit, schedule it, and make it run.
         next_full_hour = datetime.utcnow().replace(minute=0, second=0, microsecond=0) + timedelta(hours=1)
@@ -4009,9 +3999,9 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase):
 
         self.scheduler.do_full_schedule_computation()
 
-        # now check the order of the placed/scheduled units.
-        placed_units = list(models.SchedulingUnitBlueprint.objects.exclude(id=su_fixed.id).order_by('scheduled_start_time').all())
-        first_unit = placed_units[0]
+        # now check the order of the scheduled units.
+        scheduled_units = list(models.SchedulingUnitBlueprint.objects.exclude(id=su_fixed.id).order_by('scheduled_start_time').all())
+        first_unit = scheduled_units[0]
         self.assertEqual(models.SchedulingUnitStatus.Choices.SCHEDULED.value, first_unit.status.value)
 
         # the first unit partially overlaps with the running su_fixed unit from above.
@@ -4020,15 +4010,15 @@ class TestDynamicScheduling(BaseDynamicSchedulingTestCase):
         self.assertTrue((first_unit.on_sky_start_time - su_fixed.on_sky_stop_time) < timedelta(minutes=5))
 
         # and check that they cover the expected number of days
-        last_unit = placed_units[-1]
+        last_unit = scheduled_units[-1]
         total_span = last_unit.on_sky_stop_time - first_unit.on_sky_start_time
         self.assertGreaterEqual(total_span, timedelta(days=3))
         self.assertLessEqual(total_span, timedelta(days=4))
 
         # make sure they don't overlap
         prev_unit = first_unit
-        for unit in placed_units[1:]:
-            self.assertTrue(unit.placed)
+        for unit in scheduled_units[1:]:
+            self.assertEqual(models.SchedulingUnitStatus.Choices.SCHEDULED.value, unit.status.value)
             self.assertGreater(unit.on_sky_start_time, prev_unit.on_sky_stop_time)
             prev_unit = unit
 
@@ -4512,7 +4502,7 @@ class TestTriggers(BaseDynamicSchedulingTestCase):
     def test_trigger_get_scheduled_close_to_submission_time(self):
         # we expect triggers to run as soon as possible
 
-        # Set weight factor to prevent that dynamically scheduled units get placed in the future.
+        # Set weight factor to prevent that dynamically scheduled units get scheduled in the future.
         # Note: If TMSS is configured with a non-zero weight factor, triggers require strict time constraints to make
         # them get scheduled close to submission time!
         self._set_density_vs_optimal_weight_factor(0)
@@ -4536,7 +4526,7 @@ class TestTriggers(BaseDynamicSchedulingTestCase):
     def test_trigger_get_scheduled_according_to_before_constraint(self):
         # we expect triggers to start within the allowed time window.
 
-        # Set weight factor to prevent that dynamically scheduled unit gets placed to start right away by default.
+        # Set weight factor to prevent that dynamically scheduled unit gets scheduled to start right away by default.
         # Triggers with strict time constraints should not start within the permitted time window nonetheless.
         self._set_density_vs_optimal_weight_factor(0.5)
 
@@ -4664,7 +4654,7 @@ class TestTriggers(BaseDynamicSchedulingTestCase):
         # RT-PH-T1(test code, see: https://support.astron.nl/confluence/display/TMSS/DOC-01-a+Test+Plan+-+Verification+tests)
         # a trigger that gets dynamically scheduled should have priority over a regular fixed time observation
 
-        # Set weight factor to prevent that the dynamically scheduled trigger get placed in the future and the units
+        # Set weight factor to prevent that the dynamically scheduled trigger get scheduled in the future and the units
         # conflict with each other.
         self._set_density_vs_optimal_weight_factor(0)
 
@@ -4962,7 +4952,7 @@ class TestTriggers(BaseDynamicSchedulingTestCase):
         # an observation that is already happening should be interrupted when a trigger comes in so that the trigger
         # can run right away, also when the trigger is dynamically scheduled and the regular obs is fixed time.
 
-        # Set weight factor to prevent that the dynamically scheduled trigger get placed in the future and the units
+        # Set weight factor to prevent that the dynamically scheduled trigger get scheduled in the future and the units
         # conflict with each other.
         self._set_density_vs_optimal_weight_factor(0)
 
diff --git a/SAS/TMSS/backend/services/websocket/lib/websocket_service.py b/SAS/TMSS/backend/services/websocket/lib/websocket_service.py
index adcab4dbc3d..2c6380431fc 100644
--- a/SAS/TMSS/backend/services/websocket/lib/websocket_service.py
+++ b/SAS/TMSS/backend/services/websocket/lib/websocket_service.py
@@ -208,8 +208,6 @@ class TMSSEventMessageHandlerForWebsocket(TMSSEventMessageHandler):
                     json_blob['object_details']['status_value'] = model_instance.status.value
                 if hasattr(model_instance, 'state'):
                     json_blob['object_details']['state_value'] = model_instance.state.value
-                if hasattr(model_instance, 'placed'):
-                    json_blob['object_details']['placed'] = model_instance.placed
             except Exception as e:
                 logger.error("Cannot get object details for %s: %s", json_blob, e)
 
@@ -258,9 +256,6 @@ class TMSSEventMessageHandlerForWebsocket(TMSSEventMessageHandler):
     def onSchedulingUnitBlueprintUpdated(self, id: int):
         self._post_update_on_websocket(id, self.ObjTypes.SCHED_UNIT_BLUEPRINT, self.ObjActions.UPDATE)
 
-    def onSchedulingUnitBlueprintPlacedUpdated(self, id: int, placed: bool):
-        self._post_update_on_websocket(id, self.ObjTypes.SCHED_UNIT_BLUEPRINT, self.ObjActions.UPDATE)
-
     def onSchedulingUnitBlueprintDeleted(self, id: int):
         self._post_update_on_websocket(id, self.ObjTypes.SCHED_UNIT_BLUEPRINT, self.ObjActions.DELETE)
 
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/adapters/plots.py b/SAS/TMSS/backend/src/tmss/tmssapp/adapters/plots.py
index 5d4dc6be919..8b91b487f15 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/adapters/plots.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/adapters/plots.py
@@ -434,16 +434,15 @@ def scheduling_constraints_plot(scheduling_units: Union[models.SchedulingUnitBlu
 
 
 def scheduling_constraints_plot_for_date(proposed_date: date, station: str=None) -> bytes:
-    '''create a scheduling_constraints plot for all placed/scheduled/running/finished scheduling_units on the given date'''
-    scheduling_units = models.SchedulingUnitBlueprint.objects.filter(Q(status__value__in=(models.SchedulingUnitStatus.Choices.SCHEDULED.value,
+    '''create a scheduling_constraints plot for all scheduled/running/finished scheduling_units on the given date'''
+    scheduling_units = models.SchedulingUnitBlueprint.objects.filter(status__value__in=(models.SchedulingUnitStatus.Choices.SCHEDULED.value,
                                                                                           models.SchedulingUnitStatus.Choices.OBSERVING.value,
                                                                                           models.SchedulingUnitStatus.Choices.OBSERVED.value,
                                                                                           models.SchedulingUnitStatus.Choices.PROCESSING.value,
                                                                                           models.SchedulingUnitStatus.Choices.PROCESSED.value,
                                                                                           models.SchedulingUnitStatus.Choices.INGESTING.value,
                                                                                           models.SchedulingUnitStatus.Choices.INGESTED.value,
-                                                                                          models.SchedulingUnitStatus.Choices.FINISHED.value)) |
-                                                                     Q(placed=True, status__value=models.SchedulingUnitStatus.Choices.SCHEDULABLE.value))
+                                                                                          models.SchedulingUnitStatus.Choices.FINISHED.value))
     lower_bound = datetime(proposed_date.year, proposed_date.month, proposed_date.day, 0, 0, 0)
     upper_bound = lower_bound + timedelta(hours=24)
     scheduling_units = scheduling_units.filter(scheduled_stop_time__gte=lower_bound)
@@ -467,7 +466,6 @@ def lst_pressure_plot(lower_bound: datetime=None, upper_bound: datetime=None, in
     lst_values = [lower_bound_day_start + timedelta(minutes=m) for m in range(24*60-3)] # LST day is 23h56m4s, so we have 24*60 minus 3 full minutes bins
     num_bins = len(lst_values)
     pressure_series = {'scheduled': {'color': 'blue', 'data': [0 for _ in lst_values]},
-                       'placed': {'color': 'cornflowerblue', 'data': [0 for _ in lst_values]},
                        'schedulable': {'color': 'lightblue', 'data': [0 for _ in lst_values]},
                        'unschedulable': {'color': 'darkred', 'data': [0 for _ in lst_values]},
                        'observing': {'color': 'yellow', 'data': [0 for _ in lst_values]},
@@ -479,8 +477,7 @@ def lst_pressure_plot(lower_bound: datetime=None, upper_bound: datetime=None, in
     # prepare some filters to query for observation tasks with certain statuses
     filters = {
                'scheduled': Q(status__value=models.TaskStatus.Choices.SCHEDULED.value),
-               'placed': Q(scheduling_unit_blueprint__placed=True, status__value=models.SchedulingUnitStatus.Choices.SCHEDULABLE.value),
-               'schedulable': Q(scheduling_unit_blueprint__placed=False, status__value=models.SchedulingUnitStatus.Choices.SCHEDULABLE.value),
+               'schedulable': Q(status__value=models.SchedulingUnitStatus.Choices.SCHEDULABLE.value),
                'unschedulable': Q(status__value=models.TaskStatus.Choices.UNSCHEDULABLE.value),
                'observing': Q(status__value__in=(models.TaskStatus.Choices.QUEUED.value, models.TaskStatus.Choices.STARTED.value)),
                'observed': Q(status__value=models.TaskStatus.Choices.FINISHED.value),
@@ -491,7 +488,7 @@ def lst_pressure_plot(lower_bound: datetime=None, upper_bound: datetime=None, in
     for status, q_filter in filters.items():
         observation_tasks = models.TaskBlueprint.objects.filter(q_filter).filter(obsolete_since__isnull=True).filter(specifications_template__type__value=models.TaskType.Choices.OBSERVATION.value)
 
-        if status not in ('schedulable', 'unschedulable'): # 'placed' is actually 'schedulable' with placed=True, but we call it "status" here.
+        if status not in ('schedulable', 'unschedulable'):
             if lower_bound:
                 observation_tasks = observation_tasks.filter(on_sky_stop_time__gte=lower_bound)
             if upper_bound:
@@ -558,8 +555,7 @@ def lst_pressure_plot(lower_bound: datetime=None, upper_bound: datetime=None, in
     if upper_bound is None:
         # derive upper_bound from selected units
         from django.db.models import Max
-        scheduling_units = models.SchedulingUnitBlueprint.objects.filter(Q(status__value=models.SchedulingUnitStatus.Choices.SCHEDULED.value) |
-                                                                         Q(placed=True, status__value=models.SchedulingUnitStatus.Choices.SCHEDULABLE.value) )
+        scheduling_units = models.SchedulingUnitBlueprint.objects.filter(status__value=models.SchedulingUnitStatus.Choices.SCHEDULED.value)
         scheduling_units = scheduling_units.filter(obsolete_since__isnull=True)
         upper_bound = scheduling_units.aggregate(Max('on_sky_stop_time'))['on_sky_stop_time__max']
 
@@ -586,7 +582,7 @@ def lst_pressure_plot(lower_bound: datetime=None, upper_bound: datetime=None, in
     axes.grid(True)
 
     # plot the lst-pressure graph
-    labels = ['observed', 'error', 'cancelled', 'observing', 'scheduled', 'placed', 'schedulable', 'unschedulable']
+    labels = ['observed', 'error', 'cancelled', 'observing', 'scheduled', 'schedulable', 'unschedulable']
     y_values = [pressure_series[label]['data'] for label in labels]
     colors = [pressure_series[label]['color'] for label in labels]
     axes.stackplot(lst_values, y_values, labels=labels, colors=colors, alpha=0.75)
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0050_remove_schedulingunitblueprint_placed.py b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0050_remove_schedulingunitblueprint_placed.py
new file mode 100644
index 00000000000..74b1354de21
--- /dev/null
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0050_remove_schedulingunitblueprint_placed.py
@@ -0,0 +1,17 @@
+# Generated by Django 3.0.9 on 2023-10-11 17:14
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('tmssapp', '0049_cyclereport'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='schedulingunitblueprint',
+            name='placed',
+        ),
+    ]
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py
index 11b25f6fad9..082d0dd9dc7 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py
@@ -1316,7 +1316,6 @@ class SchedulingUnitBlueprint(ProjectPropertyMixin, TemplateSchemaMixin, Schedul
     results_accepted = BooleanField(default=None, null=True, db_index=True, help_text='boolean (default None) which indicates whether this unit was accepted as successful or not.')
     global_identifier = OneToOneField('SIPidentifier', null=False, editable=False, on_delete=PROTECT, help_text='The global unique identifier for LTA SIP.')
     path_to_project = 'draft__scheduling_set__project'
-    placed = BooleanField(default=False, db_index=True, null=False, help_text='Was this unit placed by the scheduler, or are the scheduled_start/stop_time just best guesses?')
 
     class Meta(NamedCommon.Meta):
         constraints = [CheckConstraint(check=Q(rank__gte=SchedulingUnitRank.HIGHEST.value) & Q(rank__lte=SchedulingUnitRank.LOWEST.value), name='schedulingunitblueprint_rank_range_constraint')]
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py
index e24e455ba86..97afde7bdfb 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py
@@ -278,7 +278,7 @@ class SchedulingUnitBlueprintSerializer(DynamicRelationalHyperlinkedModelSeriali
     class Meta:
         model = models.SchedulingUnitBlueprint
         fields = '__all__'
-        read_only_fields = ['interrupts_telescope', 'unschedulable_reason', 'error_reason', 'placed']
+        read_only_fields = ['interrupts_telescope', 'unschedulable_reason', 'error_reason']
         extra_fields = ['task_blueprints', 'output_pinned', 'unschedulable_reason', 'error_reason']
         expandable_fields = {
             'specifications_template': 'lofar.sas.tmss.tmss.tmssapp.serializers.SchedulingUnitTemplateSerializer',
@@ -311,7 +311,6 @@ class SchedulingUnitBlueprintSlimSerializer(serializers.ModelSerializer):
                             'status',
                             'unschedulable_reason',
                             'error_reason',
-                            'placed',
                             'scheduler',
                             'scheduling_constraints_template_id',
                             'process_start_time',
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py b/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py
index 8a0d9a6e6ca..0469a9f39f8 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/subtasks.py
@@ -1113,7 +1113,7 @@ def schedule_subtask_and_update_successor_start_times(subtask: Subtask, misc_una
     return scheduled_subtask
 
 
-def update_subtasks_start_times_for_scheduling_unit(scheduling_unit: SchedulingUnitBlueprint, start_time: datetime, placed: bool=None):
+def update_subtasks_start_times_for_scheduling_unit(scheduling_unit: SchedulingUnitBlueprint, start_time: datetime):
     with transaction.atomic():
         for task_blueprint in scheduling_unit.task_blueprints.all():
             defined_independend_subtasks = task_blueprint.subtasks.filter(state__value='defined').filter(inputs=None).all()
@@ -1123,19 +1123,15 @@ def update_subtasks_start_times_for_scheduling_unit(scheduling_unit: SchedulingU
                 else:
                     update_start_time_and_shift_successors_until_after_stop_time(subtask, start_time + subtask.task_blueprint.relative_start_time)
 
+        for observation_subtask in scheduling_unit.subtasks.filter(specifications_template__type__value=SubtaskType.Choices.OBSERVATION.value).all():
+            compute_scheduled_central_lst(observation_subtask)
+
         # update cached start/stop times
         scheduling_unit.refresh_from_db()
 
-        if placed is not None:
-            scheduling_unit.placed = placed
-            scheduling_unit.save()
-
-            if placed:
-                for observation_subtask in scheduling_unit.subtasks.filter(specifications_template__type__value=SubtaskType.Choices.OBSERVATION.value).all():
-                    compute_scheduled_central_lst(observation_subtask)
-
     return scheduling_unit
 
+
 def update_start_time_and_shift_successors_until_after_stop_time(subtask: Subtask, start_time: datetime):
     if subtask.state.value == SubtaskState.Choices.DEFINED.value:
         subtask.scheduled_start_time = start_time
@@ -2597,9 +2593,6 @@ def get_gaps_to_previous_and_next_observations(subtask: Subtask, exclude_subtask
     # prepare query for non-obsolete non-cancelled/error observations
     qs = Subtask.objects.filter(specifications_template__type__value=SubtaskType.Choices.OBSERVATION.value).filter(obsolete_since__isnull=True).exclude(state__value__in=(SubtaskState.Choices.CANCELLED.value, SubtaskState.Choices.ERROR.value))
 
-    # non-placed units do not have a relevant start/stoptime.
-    qs = qs.exclude(Q(placed=False) and Q(state__value=SubtaskState.Choices.DEFINED.value))
-
     if not include_defined_unschedulable:
         qs = qs.exclude(state__value=SubtaskState.Choices.DEFINED.value).exclude(state__value=SubtaskState.Choices.UNSCHEDULABLE.value)
 
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py b/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py
index e2c52c74b96..b2a823dcff7 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/tasks.py
@@ -549,15 +549,15 @@ def create_scheduling_unit_blueprint_and_tasks_and_subtasks_from_scheduling_unit
             time_constraints = constraints.get('time', {})
             if 'at' in time_constraints:
                 at = parser.parse(time_constraints['at'], ignoretz=True)
-                set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint, at, placed=True)
+                set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint, at)
             elif 'after' in time_constraints:
                 after = parser.parse(time_constraints['after'], ignoretz=True)
-                set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint, after, placed=True)
+                set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint, after)
             elif 'between' in time_constraints:
                 between_froms = [parser.parse(between['from'], ignoretz=True) for between in time_constraints['between'] if 'from' in between]
                 if between_froms:
                     earliest_from = min(between_froms)
-                    set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint, earliest_from, placed=True)
+                    set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint, earliest_from)
 
         if scheduling_unit_blueprint.interrupts_telescope:
             # check the trigger accounting, may result in scheduling_unit_blueprint ERROR status
@@ -662,15 +662,10 @@ def schedule_independent_subtasks_in_scheduling_unit_blueprint(scheduling_unit_b
         schedule_independent_subtasks_in_task_blueprint(task_blueprint, start_time=start_time+task_blueprint.relative_start_time, misc_unavailable_stations=misc_unavailable_stations)
 
     scheduling_unit_blueprint.refresh_from_db()
-
-    if not scheduling_unit_blueprint.placed:
-        scheduling_unit_blueprint.placed = True
-        scheduling_unit_blueprint.save()
-
     return scheduling_unit_blueprint
 
 
-def unschedule_subtasks_in_scheduling_unit_blueprint(scheduling_unit_blueprint: SchedulingUnitBlueprint, placed: bool=False) -> models.SchedulingUnitBlueprint:
+def unschedule_subtasks_in_scheduling_unit_blueprint(scheduling_unit_blueprint: SchedulingUnitBlueprint) -> models.SchedulingUnitBlueprint:
     '''Convenience method: Unschedule all scheduled subtasks in the scheduling_unit_blueprint'''
     with transaction.atomic():
         task_blueprints = list(scheduling_unit_blueprint.task_blueprints.all())
@@ -680,10 +675,6 @@ def unschedule_subtasks_in_scheduling_unit_blueprint(scheduling_unit_blueprint:
 
         scheduling_unit_blueprint.refresh_from_db()
 
-        if scheduling_unit_blueprint != placed:
-            scheduling_unit_blueprint.placed = placed
-            scheduling_unit_blueprint.save()
-
     return scheduling_unit_blueprint
 
 
@@ -694,7 +685,7 @@ def reschedule_subtasks_in_scheduling_unit_blueprint(scheduling_unit_blueprint:
         return schedule_independent_subtasks_in_scheduling_unit_blueprint(unscheduled_unit, misc_unavailable_stations=misc_unavailable_stations)
 
 
-def set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint: SchedulingUnitBlueprint, first_start_time: datetime, placed: bool=None) -> models.SchedulingUnitBlueprint:
+def set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint: SchedulingUnitBlueprint, first_start_time: datetime) -> models.SchedulingUnitBlueprint:
     '''Convenience method: Set the subtask.scheduled_start_time such that all scheduling_relations and dependencies are taken into account and the first subtask has a scheduled_start_time equal to first_start_time'''
     with transaction.atomic():
         for task_blueprint in scheduling_unit_blueprint.task_blueprints.all():
@@ -703,10 +694,6 @@ def set_scheduling_unit_blueprint_start_times(scheduling_unit_blueprint: Schedul
 
         scheduling_unit_blueprint.refresh_from_db()
 
-        if placed is not None:
-            scheduling_unit_blueprint.placed = placed
-            scheduling_unit_blueprint.save()
-
     return scheduling_unit_blueprint
 
 def mark_independent_subtasks_in_scheduling_unit_blueprint_as_unschedulable(scheduling_unit_blueprint: SchedulingUnitBlueprint, reason: str) -> models.SchedulingUnitBlueprint:
@@ -991,9 +978,9 @@ def get_schedulable_stations(observation_task: TaskBlueprint, proposed_start_tim
 
 
 def mark_scheduling_unit_fixed_time_scheduled_at_scheduled_starttime(scheduling_unit_blueprint: SchedulingUnitBlueprint) -> models.SchedulingUnitBlueprint:
-    '''Convenience method: When the scheduling_unit is placed or scheduled, use its scheduled_start_time as 'at' constraint and set the scheduled-type to 'fixed_time'.  As a result, the scheduler will try to scheduled it using these new constraints.
+    '''Convenience method: When the scheduling_unit is scheduled, use its scheduled_start_time as 'at' constraint and set the scheduled-type to 'fixed_time'.  As a result, the scheduler will try to scheduled it using these new constraints.
     This is the 'inverse' of mark_scheduling_unit_dynamically_scheduled'''
-    if scheduling_unit_blueprint.placed or scheduling_unit_blueprint.status.value == models.SchedulingUnitStatus.Choices.SCHEDULED.value:
+    if scheduling_unit_blueprint.status.value in (models.SchedulingUnitStatus.Choices.SCHEDULABLE.value, models.SchedulingUnitStatus.Choices.SCHEDULED.value):
         logger.info("marking unit id=%s scheduling_constraints as fixed_time scheduled at starttime='%s'", scheduling_unit_blueprint.id, scheduling_unit_blueprint.scheduled_start_time)
         scheduling_unit_blueprint.scheduling_constraints_doc['scheduler'] = 'fixed_time'
         scheduling_unit_blueprint.scheduling_constraints_doc['time']['at'] = scheduling_unit_blueprint.scheduled_start_time.isoformat()+'Z'
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py
index 0ab0a32c5c3..5d036be2b73 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py
@@ -1233,13 +1233,13 @@ class SchedulingUnitBlueprintViewSet(LOFARViewSet):
     def scheduling_constraints_plot(self, request, pk=None, station=None, proposed_start_time_or_date=None):
         scheduling_unit = get_object_or_404(models.SchedulingUnitBlueprint, pk=pk)
 
-        if not scheduling_unit.placed and proposed_start_time_or_date is None:
+        if proposed_start_time_or_date is None:
             from lofar.sas.tmss.services.scheduling.constraints import get_at_constraint_timestamp
             at_timestamp = get_at_constraint_timestamp(scheduling_unit)
             if at_timestamp is not None:
                 proposed_start_time_or_date = at_timestamp.isoformat()
             else:
-                return HttpResponseBadRequest("Cannot create scheduling constraints plot: scheduling_unit_blueprint id=%s is not placed/scheduled yet." %(pk,))
+                return HttpResponseBadRequest("Cannot create scheduling constraints plot: scheduling_unit_blueprint id=%s is not scheduled yet." %(pk,))
 
         proposed_start_time = None
         proposed_date = None
@@ -1348,7 +1348,7 @@ class SchedulingUnitBlueprintViewSet(LOFARViewSet):
             timestamp = dateutil.parser.parse(timestamp_str)
 
         for unit in SchedulingUnitBlueprint.objects.filter(status__value='schedulable', obsolete_since__isnull=True).all():
-            update_subtasks_start_times_for_scheduling_unit(unit, start_time=timestamp, placed=False)
+            update_subtasks_start_times_for_scheduling_unit(unit, start_time=timestamp)
 
         # just return a success
         return HttpResponse(status=200)
diff --git a/SAS/TMSS/backend/test/acceptance_test/acceptance_test.py b/SAS/TMSS/backend/test/acceptance_test/acceptance_test.py
index fabba0440fb..5ab859ab329 100755
--- a/SAS/TMSS/backend/test/acceptance_test/acceptance_test.py
+++ b/SAS/TMSS/backend/test/acceptance_test/acceptance_test.py
@@ -109,9 +109,9 @@ def populate_pulsar_timing_test_campaign(project: models.Project, between=None,
             elems = line.split()
             target = elems[0]
 
-            if models.SchedulingUnitDraft.objects.filter(name=target).exists():
-                logger.info("skipping creation of unit with name='%s' because it already exists", target)
-                continue
+            # if models.SchedulingUnitDraft.objects.filter(name=target).exists():
+            #     logger.info("skipping creation of unit with name='%s' because it already exists", target)
+            #     continue
 
             # read angle1 as h:m:s, and angle2 as d:m:s
             angle1 = Angle(elems[1].replace(':','h', 1).replace(':','m', 1)+'s')
@@ -137,7 +137,7 @@ def populate_pulsar_timing_test_campaign(project: models.Project, between=None,
                                                                                                   scheduling_set,
                                                                                                   name=target,
                                                                                                   description="SU for test pulsar campaign target?%s" % target,
-                                                                                                  priority_queue=models.PriorityQueueType.objects.get(value=models.PriorityQueueType.Choices.B.value),
+                                                                                                  priority_queue=models.PriorityQueueType.objects.get(value=models.PriorityQueueType.Choices.A.value),
                                                                                                   specifications_doc_overrides={
                                                                                                       'tasks': {
                                                                                                           'Observation': {
@@ -159,8 +159,8 @@ def populate_pulsar_timing_test_campaign(project: models.Project, between=None,
                                                                                                                   'transit_offset': {'to': 2*60*60, 'from': -2*60*60}}
                                                                                                       }
                                                                                                   })
-
-            create_scheduling_unit_blueprint_and_tasks_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft)
+            for i in range(5):
+                create_scheduling_unit_blueprint_and_tasks_and_subtasks_from_scheduling_unit_draft(scheduling_unit_draft)
 
 
     if max_num_targets > 0:
diff --git a/SAS/TMSS/client/lib/tmssbuslistener.py b/SAS/TMSS/client/lib/tmssbuslistener.py
index 282e6d98abe..aa868b7180a 100644
--- a/SAS/TMSS/client/lib/tmssbuslistener.py
+++ b/SAS/TMSS/client/lib/tmssbuslistener.py
@@ -146,8 +146,6 @@ class TMSSEventMessageHandler(AbstractMessageHandler):
             self.onProjectCyclesUpdated(**msg.content)
         elif stripped_subject == 'ProjectCycles.Object.Deleted':
             self.onProjectCyclesDeleted(**msg.content)
-        elif stripped_subject == 'SchedulingUnitBlueprint.Object.Placed.Updated':
-            self.onSchedulingUnitBlueprintPlacedUpdated(**msg.content)
         elif stripped_subject == 'SchedulingUnitBlueprint.Object.Constraints.Updated':
             self.onSchedulingUnitBlueprintConstraintsUpdated(**msg.content)
         elif stripped_subject == 'SchedulingUnitBlueprint.Object.Rank.Updated':
@@ -274,12 +272,6 @@ class TMSSEventMessageHandler(AbstractMessageHandler):
         '''
         pass
 
-    def onSchedulingUnitBlueprintPlacedUpdated(self, id: int, placed: bool):
-        '''onSchedulingUnitBlueprintPlacedUpdated is called upon receiving a SchedulingUnitBlueprint.Object.Placed.Updated message, which is sent when the placed property on a SchedulingUnitDrafts was updated.
-        :param id: the TMSS id of the SchedulingUnitBlueprint
-        '''
-        pass
-
     def onSchedulingUnitBlueprintPriorityQueueUpdated(self, id: int, priority_queue: str):
         '''onSchedulingUnitBlueprintPriorityQueueUpdated is called upon receiving a SchedulingUnitBlueprint.Object.PriorityQueue.Updated message, which is sent when a the priority_queue on a SchedulingUnitDrafts was updated.
         :param id: the TMSS id of the SchedulingUnitBlueprint
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/timeline.renderer.helper.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/timeline.renderer.helper.js
index ef4b250a7b9..4c1d452c702 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/timeline.renderer.helper.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/timeline.renderer.helper.js
@@ -92,11 +92,9 @@ function renderSUItem({
                           itemContext,
                           getItemProps
                       }, setPopPositionCallback, setMouseOverItemCallback, setSummaryItemCallback) {
-    const placedStatusTextStyle = item.status === "schedulable" && !item.placed ? 0.5 : 1
     const scheduleMethodBorder = item.scheduler === "dynamic" ? "1.5px dashed black" : "1.5px solid black"
     let itemDivStyle = getItemDivStyle(itemContext, item);
     itemDivStyle.fontWeight = 600
-    itemDivStyle.opacity = placedStatusTextStyle
     itemDivStyle.border = scheduleMethodBorder
 
     let itemContentStyle = getItemContentStyle(itemContext, 3, 12)
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js b/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js
index 08aba6eead0..16a898a4f20 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js
@@ -17,7 +17,6 @@ const SU_FETCH_FIELDS = ["id",
     "short_description",
     "status",
     "output_pinned",
-    "placed",
     "results_accepted",
     "unschedulable_reason",
     "scheduling_constraints_doc",
diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/ui.constants.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/ui.constants.js
index b7beb2adaca..cba11c84d76 100644
--- a/SAS/TMSS/frontend/tmss_webapp/src/utils/ui.constants.js
+++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/ui.constants.js
@@ -185,7 +185,6 @@ const UIConstants = {
             subtitle: "Indication about the settings",
             elements: [{classKey: "su-fixed_time", value: "Fixed Time"},
                 {classKey: "su-dynamic", value: "Dynamic"},
-                {classKey: "su-placed", value: "Placed"},
                 {classKey: "su-unplaced", value: "Unplaced"}
             ]
         },
-- 
GitLab