All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nl.uu.cs.ape.solver.solutionStructure.cwl.AbstractCWLCreator Maven / Gradle / Ivy

Go to download

APE is a command line tool and an API for the automated exploration of possible computational pipelines (workflows) from large collections of computational tools.

There is a newer version: 2.3.0
Show newest version
package nl.uu.cs.ape.solver.solutionStructure.cwl;

import nl.uu.cs.ape.models.logic.constructs.TaxonomyPredicate;
import nl.uu.cs.ape.solver.solutionStructure.ModuleNode;
import nl.uu.cs.ape.solver.solutionStructure.SolutionWorkflow;
import nl.uu.cs.ape.solver.solutionStructure.TypeNode;

import java.util.HashMap;
import java.util.List;

import lombok.extern.slf4j.Slf4j;

/**
 * Class to generate a CWL workflow structure from a given workflow solution.
 */
@Slf4j
public class AbstractCWLCreator extends CWLCreatorBase {
    /**
     * Maintain a list of the CWL parameter names which represent {@link TypeNode}s.
     * I.e. this hashmap has TypeNode IDs as keys and their names in the CWL file as
     * values.
     */
    private final HashMap workflowParameters = new HashMap<>();

    /**
     * Instantiates a new CWL creator.
     * 
     * @param solution The solution to represent in CWL.
     */
    public AbstractCWLCreator(SolutionWorkflow solution) {
        super(solution);
    }

    @Override
    public String getCWLVersion() {
        // CWL version 1.2.0-dev2 is the minimum version required for the features we
        // use here.
        return "v1.2.0-dev2";
    }

    /**
     * Generates the CWL representation.
     */
    @Override
    protected void generateCWLRepresentation() {
        // Workflow
        generateWorkflowInputs();
        generateWorkflowOutputs();
        generateWorkflowSteps();
    }

    /**
     * Generate the top-level inputs definition of the workflow.
     */
    private void generateWorkflowInputs() {
        cwlRepresentation.append("inputs:").append("\n");
        // Inputs
        for (TypeNode typeNode : solution.getWorkflowInputTypeStates()) {
            String inputName = String.format("input%o", workflowParameters.size() + 1);
            addParameter(typeNode, inputName);
            cwlRepresentation
                    // Name
                    .append(ind(1))
                    .append(inputName)
                    .append(":\n")
                    // Data type
                    .append(ind(2))
                    .append("type: File")
                    .append("\n")
                    // Format
                    .append(ind(2))
                    .append("format: ")
                    .append(typeNode.getFormat())
                    .append("\n");
        }
    }

    /**
     * Generate the top-level outputs definition of the workflow.
     */
    private void generateWorkflowOutputs() {
        cwlRepresentation.append("outputs:\n");
        // Outputs
        int i = 1;
        for (TypeNode typeNode : solution.getWorkflowOutputTypeStates()) {
            cwlRepresentation
                    // Name
                    .append(ind(1))
                    .append(String.format("output%o", i))
                    .append(":\n")
                    // Data type
                    .append(ind(2))
                    .append("type: File")
                    .append("\n")
                    // Format
                    .append(ind(2))
                    .append("format: ")
                    .append(typeNode.getFormat())
                    .append("\n")
                    // outputSource
                    .append(ind(2))
                    .append("outputSource: ")
                    .append(stepName(typeNode.getCreatedByModule()))
                    .append("/");
            // Get the id of the step run's output bound to this workflow output
            // (step_name/output_name_ID)
            int outId = typeNode.getCreatedByModule().getOutputTypes().get(i - 1).getAutomatonState()
                    .getLocalStateNumber();
            cwlRepresentation
                    .append(inOutName(typeNode.getCreatedByModule(), "out", outId + 1))
                    .append("\n");
            i++;
        }
    }

    /**
     * Generate the top-level steps of the workflow.
     */
    private void generateWorkflowSteps() {
        cwlRepresentation.append("steps:").append("\n");
        for (ModuleNode moduleNode : solution.getModuleNodes()) {
            generateStep(moduleNode);
        }
    }

    /**
     * Generate one workflow step.
     * 
     * @param moduleNode The {@link ModuleNode} related to the step.
     */
    private void generateStep(ModuleNode moduleNode) {
        final int baseInd = 1;
        // Name
        cwlRepresentation
                .append(ind(baseInd))
                .append(stepName(moduleNode))
                .append(":\n");
        generateStepIn(moduleNode);
        generateStepOut(moduleNode);
        generateStepRun(moduleNode);
    }

    /**
     * Generate the "in" section of a workflow step.
     * 
     * @param moduleNode The {@link ModuleNode} that is the step.
     */
    private void generateStepIn(ModuleNode moduleNode) {
        final int baseInd = 2;
        // "in" key
        cwlRepresentation.append(ind(baseInd)).append("in").append(":\n");
        // If there are no inputs, give an empty array as input
        if (moduleNode.getInputTypes().isEmpty()) {
            // Remove the last newline so the array is on the same line as "in:"
            deleteLastNCharactersFromCWL(1);
            cwlRepresentation
                    .append(" ")
                    .append("[]")
                    .append("\n");
            return;
        }

        int i = 0;
        for (TypeNode typeNode : moduleNode.getInputTypes()) {
            cwlRepresentation
                    .append(ind(baseInd + 1))
                    .append(inOutName(moduleNode, "in", i + 1))
                    .append(": ")
                    .append(workflowParameters.get(typeNode.getNodeID()))
                    .append("\n");
            i++;
        }
    }

    /**
     * Generate the "out" section of a workflow step.
     * 
     * @param moduleNode The {@link ModuleNode} that is the step.
     */
    private void generateStepOut(ModuleNode moduleNode) {
        final int baseInd = 2;
        cwlRepresentation
                // "out" key
                .append(ind(baseInd))
                .append("out: ")
                // The outputs array
                .append("[");
        int i = 1;
        for (TypeNode typeNode : moduleNode.getOutputTypes()) {
            String name = inOutName(moduleNode, "out", i);
            addParameter(typeNode, String.format("%s/%s", stepName(moduleNode), name));
            cwlRepresentation
                    .append(name)
                    .append(", ");
            i++;
        }
        deleteLastNCharactersFromCWL(2);
        cwlRepresentation.append("]").append("\n");
    }

    /**
     * Generate the run field of a workflow step.
     * 
     * @param moduleNode The {@link ModuleNode} related to the step.
     */
    private void generateStepRun(ModuleNode moduleNode) {
        final int baseInd = 2;
        cwlRepresentation
                // Main key
                .append(ind(baseInd))
                .append("run:")
                .append("\n")
                // Class
                .append(ind(baseInd + 1))
                .append("class: Operation")
                .append("\n");
        // Inputs
        cwlRepresentation
                .append(ind(baseInd + 1))
                .append("inputs:")
                .append("\n");
        generateTypeNodes(moduleNode, moduleNode.getInputTypes(), true, baseInd + 2);
        // Outputs
        cwlRepresentation
                .append(ind(baseInd + 1))
                .append("outputs:")
                .append("\n");
        generateTypeNodes(moduleNode, moduleNode.getOutputTypes(), false, baseInd + 2);
        // Hints and intent
        generateStepHints(moduleNode, baseInd + 1);
        generateStepIntent(moduleNode, baseInd + 1);
    }

    /**
     * Generate the hints related to this step's run.
     * 
     * @param moduleNode The {@link ModuleNode} that is the step.
     * @param baseInd    The indentation at which the hints should start.
     */
    private void generateStepHints(ModuleNode moduleNode, int baseInd) {
        cwlRepresentation
                // "hints" key
                .append(ind(baseInd))
                .append("hints:")
                .append("\n")
                // "SoftwareRequirement" key
                .append(ind(baseInd + 1))
                .append("SoftWareRequirement:")
                .append("\n")
                // "packages" key
                .append(ind(baseInd + 2))
                .append("packages:")
                .append("\n")
                // The required package
                .append(ind(baseInd + 3))
                .append(
                        String.format("%s: [\"%s\"]",
                                moduleNode.getNodeLabel(),
                                moduleNode.getUsedModule().getPredicateID()))
                .append("\n");
    }

    /**
     * Generate the intent for a workflow step's run.
     * 
     * @param moduleNode The {@link ModuleNode} that is the workflow step.
     * @param baseInd    The indentation level at which the intent should start.
     */
    private void generateStepIntent(ModuleNode moduleNode, int baseInd) {
        cwlRepresentation
                .append(ind(baseInd))
                .append("intent: ")
                .append("[");
        for (TaxonomyPredicate predicate : moduleNode.getUsedModule().getSuperPredicates()) {
            cwlRepresentation
                    .append("\"")
                    .append(predicate.getPredicateID())
                    .append("\"")
                    .append(", ");
        }
        deleteLastNCharactersFromCWL(2);
        cwlRepresentation.append("]").append("\n");
    }

    /**
     * Generate the inputs or outputs of a step's run.
     * 
     * @param moduleNode   The {@link ModuleNode} that is the workflow step.
     * @param typeNodeList The {@link TypeNode}s that are either the input or output
     *                     nodes.
     * @param input        Whether the type nodes are inputs. If false, they are
     *                     consider outputs.
     * @param baseInd      The indentation level of the "inputs:" or "outputs:" line
     *                     preceding this function.
     */
    private void generateTypeNodes(ModuleNode moduleNode, List typeNodeList, boolean input, int baseInd) {
        // If the input or output nodes are empty, give an empty array as input or
        // output
        if (typeNodeList.isEmpty()) {
            // Remove the last newline so the array is on the same line as "inputs:"
            deleteLastNCharactersFromCWL(1);
            cwlRepresentation
                    .append(" ")
                    .append("[]")
                    .append("\n");
            return;
        }

        int i = 0;
        for (TypeNode typeNode : typeNodeList) {
            // Don't include empty nodes
            if (typeNode.isEmpty()) {
                return;
            }

            i++;
            cwlRepresentation
                    // Name
                    .append(ind(baseInd))
                    .append(inOutName(moduleNode, input ? "in" : "out", i))
                    .append(":\n")
                    // Data type
                    .append(ind(baseInd + 1))
                    .append("type: File")
                    .append("\n")
                    // Format
                    .append(ind(baseInd + 1))
                    .append("format: ")
                    .append(typeNode.getFormat())
                    .append("\n");
        }
    }

    /**
     * Generate the name of the input or output of a step's run input or output.
     * I.e. "moduleName_indicator_n".
     * 
     * @param moduleNode The {@link ModuleNode} that is the workflow step.
     * @param indicator  Indicator whether it is an input or an output.
     * @param n          The n-th input or output this is.
     * @return The name of the input or output.
     */
    private String inOutName(ModuleNode moduleNode, String indicator, int n) {
        return String.format("%s_%s_%o",
                moduleNode.getNodeLabel(),
                indicator,
                n);
    }

    /**
     * Add a parameter to the parameter hashmap.
     * Will log an error to the error output if {@link TypeNode} is already known.
     * 
     * @param typeNode The {@link TypeNode} that is the input parameter.
     * @param name     The name of the parameter.
     */
    private void addParameter(TypeNode typeNode, String name) {
        if (workflowParameters.putIfAbsent(typeNode.getNodeID(), name) != null) {
            log.warn("Duplicate key \"{}\" in workflow inputs!\n", typeNode.getNodeID());
        }
    }

    /**
     * Generate the name for a step in the workflow.
     * 
     * @param moduleNode The {@link ModuleNode} that is the workflow step.
     * @return The name of the workflow step.
     */
    private String stepName(ModuleNode moduleNode) {
        return moduleNode.getUsedModule().getPredicateLabel() +
                moduleNode.getAutomatonState().getLocalStateNumber();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy