Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions +PropulsionPkg/CheckEdgePowerConsistency.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
function [] = CheckEdgePowerConsistency(Arch, LamUps, LamDwn, EtaUps, EtaDwn, SinkPower)
%
% [] = CheckEdgePowerConsistency(Arch, LamUps, LamDwn, EtaUps, EtaDwn, SinkPower)
% written by Triet Ho
% last updated: 16 sep 2026
%
% Check that upstream and downstream matrices describe the same power on
% every active edge at one mission control point. Downstream propagation
% gives the component powers needed to meet the sink demand. Each edge's
% delivered power must then agree with upstream propagation from its source.
%
% INPUTS:
% Arch - source-to-target propulsion architecture matrix.
% size/type/units: n-by-n / logical or double / []
% LamUps - evaluated upstream operational matrix.
% size/type/units: n-by-n / double / []
% LamDwn - evaluated downstream operational matrix.
% size/type/units: n-by-n / double / []
% EtaUps - upstream edge efficiencies.
% size/type/units: n-by-n / double / []
% EtaDwn - downstream edge efficiencies.
% size/type/units: n-by-n / double / []
% SinkPower - power required at the final sink.
% size/type/units: 1-by-1 / double / [W]
%
% OUTPUTS:
% none; throws an error when an edge is inconsistent.
%

% An infinite demand is handled by PropAnalysis as an infeasible mission
% point, so it has no finite edge flow to compare.
if (isinf(SinkPower))
return;
end

% Use the same downstream propagation as the mission analysis, including
% path losses, to recover power at every component from the sink demand.
ncomp = length(Arch);
Power = zeros(ncomp, 1);
Power(end) = SinkPower;
Power = PropulsionPkg.PowerFlow(Power, Arch', LamDwn, EtaDwn, -1);

% Compare delivered power on each source-to-target edge. The downstream
% split is a fraction of target demand; the upstream split is a fraction
% of source power and must include the upstream edge efficiency.
[Source, Target] = find(Arch);
DownIndex = sub2ind([ncomp, ncomp], Target, Source);
UpIndex = sub2ind([ncomp, ncomp], Source, Target);
DownEdge = Power(Target) .* LamDwn(DownIndex);
UpEdge = Power(Source) .* LamUps(UpIndex) .* EtaUps(UpIndex);

% Match PowerFlow's 1e-6 W convergence floor while allowing small
% relative rounding error at large power. Row sums alone cannot detect
% mismatched individual edge flows.
Tolerance = max(1.0e-06, 1.0e-08 * max(abs(DownEdge), abs(UpEdge)));
if (any(~isfinite(DownEdge)) || any(~isfinite(UpEdge)) || ...
any(abs(DownEdge - UpEdge) > Tolerance))
error("FAST:EdgePowerInconsistent", ...
"Edge power is not consistent. Your power splits are over-constrained.");
end

end
2 changes: 2 additions & 0 deletions +PropulsionPkg/Contents.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
%
% Functions
% -------------------------------------------------------------------------
% CheckEdgePowerConsistency - PropulsionPkg.CheckEdgePowerConsistency is a function.
% CreatePropArch - PropulsionPkg.CreatePropArch is a function.
% EngineLapse - PropulsionPkg.EngineLapse is a function.
% EvalSplit - PropulsionPkg.EvalSplit is a function.
Expand All @@ -16,5 +17,6 @@
% PropulsionSizing - PropulsionPkg.PropulsionSizing is a function.
% RecomputeSplits - PropulsionPkg.RecomputeSplits is a function.
% TestCreatePropArch - PropulsionPkg.TestCreatePropArch is a function.
% TestEdgePowerConsistency - PropulsionPkg.TestEdgePowerConsistency is a function.
% TestEngineMotorRegressions - PropulsionPkg.TestEngineMotorRegressions is a function.
% TestPowerAvailable - PropulsionPkg.TestPowerAvailable is a function.
21 changes: 20 additions & 1 deletion +PropulsionPkg/PropAnalysis.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
%
% [Aircraft] = PropAnalysis(Aircraft)
% written by Paul Mokotoff, prmoko@umich.edu
% last updated: 10 mar 2026
% last updated: 16 sep 2026
%
% Analyze the propulsion system for a given set of flight conditions.
% Remember how the propulsion system performs in the mission history.
Expand Down Expand Up @@ -47,9 +47,15 @@
% get the downstream operational matrix
OperDwn = Aircraft.Specs.Propulsion.PropArch.OperDwn;

% get the upstream operational matrix for edge-flow consistency
OperUps = Aircraft.Specs.Propulsion.PropArch.OperUps;

% get the downstream efficiency matrix
EtaDwn = Aircraft.Specs.Propulsion.PropArch.EtaDwn;

% get the upstream efficiency matrix
EtaUps = Aircraft.Specs.Propulsion.PropArch.EtaUps;

% get which propellers are connected to the gas turbine engines
WhichProp = Aircraft.Specs.Propulsion.PropArch.WhichProp;

Expand Down Expand Up @@ -118,10 +124,23 @@

% get the necessary splits
LamDwn = Aircraft.Mission.History.SI.Power.LamDwn(SegBeg:SegEnd, :);
LamUps = Aircraft.Mission.History.SI.Power.LamUps(SegBeg:SegEnd, :);

% check for a windmilling engine
Windmill = Aircraft.Mission.History.SI.Power.Windmill(SegBeg:SegEnd, :);

% The two operational matrices describe the same mission point. Reject
% edge flows that disagree even when both matrix row sums are valid.
for ipnt = 1:length(PreqSnk)
SplitUps = PropulsionPkg.EvalSplit(OperUps, LamUps(ipnt, :));
SplitDwn = PropulsionPkg.EvalSplit(OperDwn, LamDwn(ipnt, :));
if (any(Windmill(ipnt, :)))
SplitDwn = RedistForWindmill(SplitDwn, Windmill(ipnt, :), nsrc, ncomp);
end
PropulsionPkg.CheckEdgePowerConsistency( ...
Arch, SplitUps, SplitDwn, EtaUps, EtaDwn, PreqSnk(ipnt));
end

% aircraft weight
Mass = Aircraft.Mission.History.SI.Weight.CurWeight(SegBeg:SegEnd);

Expand Down
8 changes: 5 additions & 3 deletions +PropulsionPkg/PropArchConnections.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
%
% [Aircraft] = PropArchConnections(Aircraft)
% written by Paul Mokotoff, prmoko@umich.edu
% last updated: 17 jan 2025
% last updated: 16 sep 2026
%
% Given a propulsion architecture, identify any parallel electric motor /
% engine connections. These connections are used to reduce the power
Expand Down Expand Up @@ -77,7 +77,9 @@
if (~isempty(Driving) && ~isempty(Helping))

% list the electric motors
ParConns{Driving} = [ParConns{Driving}; Helping];
for idrive = Driving(:)'
ParConns{idrive} = [ParConns{idrive}; Helping];
end

end
end
Expand All @@ -86,4 +88,4 @@
Aircraft.Specs.Propulsion.PropArch.ParConns = ParConns;


end
end
Loading