@@ -586,9 +586,27 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext
586586 extraSpecs.push_back (rootSink);
587587 }
588588
589- // Inject a collector which merges all META messages and republishes them as
590- // the AOD metadata keys/vals the AOD writer writes into the AO2D file.
591- if (hasMetaOutput) {
589+ auto hasWritableAODOutput = std::ranges::any_of (workflow, [](DataProcessorSpec const & spec) {
590+ return std::ranges::any_of (spec.outputs , [](OutputSpec const & output) {
591+ return DataSpecUtils::partialMatch (output, writableAODOrigins);
592+ });
593+ });
594+ auto hasTFNumberOutput = std::ranges::any_of (workflow, [](DataProcessorSpec const & spec) {
595+ return std::ranges::any_of (spec.outputs , [](OutputSpec const & output) {
596+ return DataSpecUtils::match (output, " TFN" , " TFNumber" , 0 );
597+ });
598+ });
599+ auto hasTFFileNameOutput = std::ranges::any_of (workflow, [](DataProcessorSpec const & spec) {
600+ return std::ranges::any_of (spec.outputs , [](OutputSpec const & output) {
601+ return DataSpecUtils::match (output, " TFF" , " TFFilename" , 0 );
602+ });
603+ });
604+
605+ // Inject a collector which merges META messages only when the same topology
606+ // can also write the resulting AMD metadata to an AO2D. Otherwise split
607+ // detector subworkflows get an internal collector whose AMD output has no
608+ // meaningful AOD writer route and the service device can hang the workflow.
609+ if (hasMetaOutput && (hasWritableAODOutput || (hasTFNumberOutput && hasTFFileNameOutput))) {
592610 extraSpecs.push_back (AnalysisSupportHelpers::getMetadataCollectorSink (ctx));
593611 }
594612
@@ -753,13 +771,38 @@ void WorkflowHelpers::injectAODWriter(WorkflowSpec& workflow, ConfigContext cons
753771 // create DataOutputDescriptor
754772 std::shared_ptr<DataOutputDirector> dod = AnalysisSupportHelpers::getDataOutputDirector (ctx);
755773
774+ auto hasWritableAOD = std::ranges::any_of (dec.outputsInputs , [](InputSpec const & spec) {
775+ return DataSpecUtils::partialMatch (spec, writableAODOrigins);
776+ });
777+ auto hasTFNumber = std::ranges::any_of (dec.outputsInputs , [](InputSpec const & spec) {
778+ return DataSpecUtils::partialMatch (spec, header::DataOrigin{" TFN" }) &&
779+ DataSpecUtils::partialMatch (spec, header::DataDescription{" TFNumber" });
780+ });
781+ auto hasTFFileName = std::ranges::any_of (dec.outputsInputs , [](InputSpec const & spec) {
782+ return DataSpecUtils::partialMatch (spec, header::DataOrigin{" TFF" }) &&
783+ DataSpecUtils::partialMatch (spec, header::DataDescription{" TFFilename" });
784+ });
785+ auto canWriteMetadataOnly = hasTFNumber && hasTFFileName;
786+
756787 // select outputs of type AOD which need to be saved
757788 dec.outputsInputsAOD .clear ();
758789 for (auto ii = 0u ; ii < dec.outputsInputs .size (); ii++) {
759- if (DataSpecUtils::partialMatch (dec.outputsInputs [ii], AODOrigins)) {
760- auto ds = dod->getDataOutputDescriptors (dec.outputsInputs [ii]);
761- if (ds.size () > 0 || dec.isDangling [ii]) {
762- dec.outputsInputsAOD .emplace_back (dec.outputsInputs [ii]);
790+ auto const & input = dec.outputsInputs [ii];
791+ if (!DataSpecUtils::partialMatch (input, AODOrigins)) {
792+ continue ;
793+ }
794+ if (DataSpecUtils::partialMatch (input, header::DataOrigin{" AMD" }) && !hasWritableAOD && !canWriteMetadataOnly) {
795+ continue ;
796+ }
797+ auto ds = dod->getDataOutputDescriptors (input);
798+ if (ds.size () > 0 || dec.isDangling [ii]) {
799+ dec.outputsInputsAOD .emplace_back (input);
800+ if (DataSpecUtils::partialMatch (input, header::DataOrigin{" AMD" })) {
801+ // Metadata collected from META outputs is republished as AMD sporadic
802+ // data. Keep the AOD writer input sporadic so the ordered completion
803+ // policy can consume it as side information without requiring it for
804+ // every TF.
805+ dec.outputsInputsAOD .back ().lifetime = Lifetime::Sporadic;
763806 }
764807 }
765808 }
@@ -772,15 +815,16 @@ void WorkflowHelpers::injectAODWriter(WorkflowSpec& workflow, ConfigContext cons
772815 auto fileSink = AnalysisSupportHelpers::getGlobalAODSink (ctx);
773816 workflow.push_back (fileSink);
774817
775- auto it = std::find_if (dec.outputsInputs .begin (), dec.outputsInputs .end (), [](InputSpec const & spec) -> bool {
776- return DataSpecUtils::partialMatch (spec, o2::header::DataOrigin (" TFN" ));
777- });
778- dec.isDangling [std::distance (dec.outputsInputs .begin (), it)] = false ;
779-
780- it = std::find_if (dec.outputsInputs .begin (), dec.outputsInputs .end (), [](InputSpec const & spec) -> bool {
781- return DataSpecUtils::partialMatch (spec, o2::header::DataOrigin (" TFF" ));
782- });
783- dec.isDangling [std::distance (dec.outputsInputs .begin (), it)] = false ;
818+ auto markAsMatched = [&](header::DataOrigin origin) {
819+ auto it = std::find_if (dec.outputsInputs .begin (), dec.outputsInputs .end (), [&](InputSpec const & spec) -> bool {
820+ return DataSpecUtils::partialMatch (spec, origin);
821+ });
822+ if (it != dec.outputsInputs .end ()) {
823+ dec.isDangling [std::distance (dec.outputsInputs .begin (), it)] = false ;
824+ }
825+ };
826+ markAsMatched (header::DataOrigin{" TFN" });
827+ markAsMatched (header::DataOrigin{" TFF" });
784828 }
785829}
786830
0 commit comments