Skip to the content.

Field Development Design Orchestration

Overview

FieldDevelopmentDesignOrchestrator coordinates a process simulation, TORG application, mechanical design, validation messages, and a text report. It lives in neqsim.process.mechanicaldesign, together with DesignPhase, DesignCase, and DesignValidationResult.

Current scope: one workflow call runs the supplied process once. Adding multiple design cases creates result labels containing the same base-case design totals; it does not change flow, composition, pressure, or temperature and simulate each scenario. The complete example below explicitly updates throughput and runs a separate workflow for each case.

MechanicalDesignGuideDocumentationTest compiles and runs that example, including mass-balance, finite-result, and compressor-power trend checks. It is a synthetic API demonstration, not a qualified field design or independent validation of equipment correlations.

Orchestrator Architecture

Object Role
ProcessSystem User-defined fluid, feeds, equipment, and operating conditions
DesignPhase Reporting metadata and validation requirements
DesignCase Scenario label and indicative load factor
TorgManager Active requirements and standards application
SystemMechanicalDesign Per-equipment calculations and aggregate results
DesignValidationResult Messages, metrics, and severity-based status

Design Phases

The table reflects the current enum values. Accuracy ranges are planning metadata, not measured error bounds guaranteed by NeqSim.

Phase getAccuracyRange() Detailed compliance flag Full mechanical-design flag
SCREENING ±40-50% No No
CONCEPT_SELECT ±25-35% No No
PRE_FEED ±20-30% No No
FEED ±15-20% Yes Yes
DETAIL_DESIGN ±10-15% Yes Yes
AS_BUILT ±5% Yes No

Using Design Phases

Use setDesignPhase(...) on the orchestrator. DesignPhase exposes getAccuracyRange(), requiresDetailedCompliance(), and requiresFullMechanicalDesign(). There are no isLaterThan or isEarlierThan methods. The workflow still calls system mechanical design in every phase; these flags control validation, not selection of a different solver.

Design Cases

These are the enum’s current indicative values. Replace them with project operating cases where known; a scalar throughput change does not model startup, shutdown, or relief physics.

Case Typical load factor isSizingCritical() requiresReliefSizing()
NORMAL 1.0 No No
MAXIMUM 1.15 Yes No
MINIMUM 0.4 No No
STARTUP 0.0 No No
SHUTDOWN 0.0 No No
UPSET 1.25 Yes Yes
EMERGENCY 1.5 No Yes
WINTER 1.0 No No
SUMMER 1.0 No No
EARLY_LIFE 1.2 Yes No
LATE_LIFE 0.6 No No

Using Design Cases

Read the factor with getTypicalLoadFactor(). isTurndownCase() is true for MINIMUM and LATE_LIFE. To select sizing or relief cases, iterate over DesignCase.values() and apply the corresponding predicate; static methods such as getSizingCriticalCases() do not exist.

Complete Workflow Example

Step 1: Create Orchestrator

The constructor requires both ProcessSystem and project ID: new FieldDevelopmentDesignOrchestrator(process, "DOCS-FIELD"). Define the fluid, feed rate, and all equipment before creating it; see the complete class below. The example treats the feed as an external boundary: run feed.run() explicitly, and include only the separator and compressor in the orchestrated system. The current FEED compliance check otherwise attempts to require design standards on the plain feed stream, whose base mechanical-design accessor returns a new placeholder object on each call.

Step 2: Configure Design Phase and Cases

The constructor initially selects NORMAL and MAXIMUM. Replace the list using setDesignCases(...); addDesignCase(...) adds a unique label. In the example, one label is selected per actual simulation so each result has an unambiguous operating point.

Step 3: Load and Apply TORG

For a programmatic document, use orchestrator.getTorgManager().setActiveTorg(torg) before running. For file input, configure that manager with a CsvTorgDataSource, then call load(projectId) and activate the returned document, or use loadAndApply(projectId, process).

The orchestrator’s loadTorg(projectId) and loadTorg(projectId, dataSource) currently only report whether a document was found. They do not activate it. Check getActiveTorg() before relying on TORG application. See TORG Integration for CSV formats and the current environmental-temperature unit limitation. This example does not include environmental metadata in the active TORG.

Step 4: Run Complete Workflow

runCompleteDesignWorkflow() returns a boolean. The sequence initializes results, runs one process calculation, applies the active TORG, runs mechanical design, optionally generates engineering deliverables, validates, and records a summary.

Inspect both this boolean and getSystemMechanicalDesign().getLastCalculationResult(). The system design uses best-effort execution, so a partial equipment calculation can be recorded without causing the orchestrator itself to throw. A true workflow result is not sufficient proof that all equipment calculations completed.

Step 5: Get Results

Use getValidationResult(), getCaseResults(), getWorkflowHistory(), and generateDesignReport(). DesignCaseResult and WorkflowStep are nested classes of the orchestrator. validateDesign() is private; it is invoked by the workflow.

Design Validation Results

Severity Levels

Severity Meaning in the result container Makes isValid() false
INFO Information No
WARNING Review required No
ERROR Reported error Yes
CRITICAL Reported critical issue Yes

Using Validation Results

Retrieve all messages with getMessages(), filter with getMessages(Severity.ERROR), and count with getCount(Severity.ERROR). hasWarnings() and hasErrors() are available; getMessagesBySeverity(), getErrorCount(), and hasCriticalIssues() are not.

getSummary() provides a compact count summary. The workflow checks standard assignment and some basic weight/pressure conditions. It reports TORG environmental ranges and safety factors as information; it does not establish comprehensive TORG compliance. Also inspect positive, finite calculated values and independent engineering acceptance criteria.

Design Report Generation

generateDesignReport() returns a string with project/run identifiers, phase, case labels, aggregate equipment results, validation messages, and workflow history. It does not contain independently solved results for labels that were merely added to one workflow. Keep each report with its actual input conditions and software revision.

Workflow Customization

Custom Workflow Steps

Call application prechecks before runCompleteDesignWorkflow() and export results afterward. There are no addPreProcessStep or addPostProcessStep callback methods.

Selective Case Execution

Use setDesignCases(Collections.singletonList(designCase)) for an explicitly configured operating point, as in the example. clearDesignCases() is not a public method.

Phase-Specific Behavior

Choose a phase to select validation requirements and report metadata. There is no setDetailedCalculations(...) switch on this orchestrator.

Integration with Process Simulation

Updating Process Conditions

Change the actual feed/equipment inputs before each workflow. The example scales mass flow from the 10,000 kg/hr base rate while holding SRK composition, 50 bara inlet pressure, 30 °C inlet temperature, 80 bara compressor outlet pressure, and 75% isentropic efficiency constant. A corresponding change in compressor power is expected for this fixed operating state.

Equipment Sizing Envelope

There is no orchestrator getSizingEnvelope() API. Store the results of the separately executed cases in an application map and compare the quantities relevant to each item: gas volume flow, liquid residence requirement, pressure, temperature, power, and mechanical dimensions. Do not select every design variable from a single case merely because it has the highest total weight.

Error Handling

Check the workflow return value and inspect validation/history even after failure. Check the structured mechanical-design result’s isComplete() before accepting totals. The previously shown TorgNotFoundException, DesignConvergenceException, and StandardNotSupportedException handlers are not part of this orchestrator’s public workflow.

Best Practices

1. Progressive Refinement

Replace screening assumptions with project data as the study matures. Changing the phase label alone does not refine model physics or input quality.

2. Document All Assumptions

Keep a companion record of composition, flow basis, operating cases, design margins, material choices, standards, and correlation limits. addAssumption(...) is not an orchestrator API.

3. Version Control Integration

Store the input model, software commit, TORG revision, and per-case reports together. The orchestrator supplies a run UUID but has no setRunMetadata(...) method.

4. Reproducibility

Retain executable model construction code. There are no saveConfiguration(...) or loadConfiguration(...) methods on this orchestrator.

Complete Example

The example logs summaries at INFO level; enable INFO output in your Log4j2 configuration to see them. Its result checks run regardless of the logging level.

import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import neqsim.process.equipment.ProcessEquipmentInterface;
import neqsim.process.equipment.compressor.Compressor;
import neqsim.process.equipment.separator.Separator;
import neqsim.process.equipment.stream.Stream;
import neqsim.process.mechanicaldesign.DesignCase;
import neqsim.process.mechanicaldesign.DesignPhase;
import neqsim.process.mechanicaldesign.DesignValidationResult;
import neqsim.process.mechanicaldesign.FieldDevelopmentDesignOrchestrator;
import neqsim.process.mechanicaldesign.SystemMechanicalDesignResult;
import neqsim.process.mechanicaldesign.designstandards.StandardType;
import neqsim.process.mechanicaldesign.torg.TechnicalRequirementsDocument;
import neqsim.process.processmodel.ProcessSystem;
import neqsim.thermo.system.SystemSrkEos;

public class FieldDevelopmentDesignExample {
  private static final Logger logger = LogManager.getLogger(FieldDevelopmentDesignExample.class);

  public static void main(String[] args) {
    SystemSrkEos fluid = new SystemSrkEos(303.15, 50.0);
    fluid.addComponent("methane", 0.70);
    fluid.addComponent("ethane", 0.10);
    fluid.addComponent("propane", 0.10);
    fluid.addComponent("n-butane", 0.05);
    fluid.addComponent("n-pentane", 0.05);
    fluid.setMixingRule("classic");
    Stream feed = new Stream("Feed", fluid);
    Separator separator = new Separator("HP Separator", feed);
    separator.setInternalDiameter(1.0);
    Compressor compressor = new Compressor("Export Compressor", separator.getGasOutStream());
    compressor.setOutletPressure(80.0, "bara");
    compressor.setIsentropicEfficiency(0.75);
    ProcessSystem process = new ProcessSystem();
    process.add(separator);
    process.add(compressor);
    for (ProcessEquipmentInterface equipment : process.getUnitOperations()) {
      equipment.getMechanicalDesign().setCompanySpecificDesignStandards("default");
    }
    separator.getMechanicalDesign().setMaxOperationPressure(50.0, "bara");
    compressor.getMechanicalDesign().setMaxOperationPressure(80.0, "bara");
    TechnicalRequirementsDocument torg = TechnicalRequirementsDocument.builder()
        .projectId("DOCS-FIELD").projectName("Synthetic gas processing study")
        .addStandard(StandardType.ASME_VIII_DIV1.getDesignStandardCategory(), StandardType.ASME_VIII_DIV1)
        .addStandard(StandardType.API_12J.getDesignStandardCategory(), StandardType.API_12J)
        .addStandard(StandardType.API_617.getDesignStandardCategory(), StandardType.API_617).build();

    double baseFlowKgPerHour = 10000.0;
    Map<DesignCase, Double> powersKw = new EnumMap<>(DesignCase.class);
    for (DesignCase designCase : Arrays.asList(DesignCase.NORMAL, DesignCase.MAXIMUM, DesignCase.MINIMUM)) {
      feed.setFlowRate(baseFlowKgPerHour * designCase.getTypicalLoadFactor(), "kg/hr");
      feed.run();
      FieldDevelopmentDesignOrchestrator orchestrator =
          new FieldDevelopmentDesignOrchestrator(process, "DOCS-FIELD-" + designCase.name());
      orchestrator.setDesignPhase(DesignPhase.FEED);
      orchestrator.setDesignCases(Collections.singletonList(designCase));
      orchestrator.getTorgManager().setActiveTorg(torg);
      boolean workflowCompleted = orchestrator.runCompleteDesignWorkflow();
      DesignValidationResult validation = orchestrator.getValidationResult();
      if (!workflowCompleted || orchestrator.getSystemMechanicalDesign() == null) {
        throw new IllegalStateException(validation.getMessages().toString());
      }
      SystemMechanicalDesignResult calculation =
          orchestrator.getSystemMechanicalDesign().getLastCalculationResult()
              .orElseThrow(() -> new IllegalStateException("No mechanical calculation result"));
      if (!calculation.isComplete()) {
        throw new IllegalStateException("Some equipment designs did not complete");
      }
      double massOut = separator.getGasOutStream().getFlowRate("kg/hr")
          + separator.getLiquidOutStream().getFlowRate("kg/hr");
      double powerKw = compressor.getPower("kW");
      double weightKg = orchestrator.getCaseResults().get(designCase).getTotalWeight();
      if (!Double.isFinite(massOut)
          || Math.abs(massOut - feed.getFlowRate("kg/hr")) > 1.0e-5
          || !Double.isFinite(powerKw) || powerKw <= 0.0
          || !Double.isFinite(weightKg) || weightKg <= 0.0) {
        throw new IllegalStateException("Invalid mass balance, power, or mechanical weight");
      }
      powersKw.put(designCase, powerKw);
      logger.info("{}: feed={} kg/hr, compressor={} kW, estimated weight={} kg, warnings={}",
          designCase.name(), feed.getFlowRate("kg/hr"), powerKw, weightKg,
          validation.getCount(DesignValidationResult.Severity.WARNING));
      logger.info("{}", orchestrator.generateDesignReport());
    }
    if (!(powersKw.get(DesignCase.MINIMUM) < powersKw.get(DesignCase.NORMAL)
        && powersKw.get(DesignCase.NORMAL) < powersKw.get(DesignCase.MAXIMUM))) {
      throw new IllegalStateException("Expected compressor power to increase with throughput");
    }
  }
}

The executed feed rates are 10,000, 11,500, and 4,000 kg/hr. Each report corresponds to one actual process run. Review any warnings even when the boolean result is true; the example’s checks establish software execution and basic physical consistency only.

See Also