Skip to content
Merged
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
19 changes: 19 additions & 0 deletions .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[bumpversion]
current_version = 4.3.0
commit = False
tag = False
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?
serialize =
{major}.{minor}.{patch}-{release}
{major}.{minor}.{patch}

[bumpversion:file:pycycle/__init__.py]
search = __version__ = '{current_version}'
replace = __version__ = '{new_version}'

[bumpversion:part:release]
optional_value = rel
values =
dev
rel

86 changes: 43 additions & 43 deletions example_cycles/high_bypass_turbofan.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,29 @@ def initialize(self):

def setup(self):
#Setup the problem by including all the relavant components here - comp, burner, turbine etc

#Create any relavent short hands here:
design = self.options['design']

USE_TABULAR = False
if USE_TABULAR:
if USE_TABULAR:
self.options['thermo_method'] = 'TABULAR'
self.options['thermo_data'] = pyc.AIR_JETA_TAB_SPEC
FUEL_TYPE = 'FAR'
else:
else:
self.options['thermo_method'] = 'CEA'
self.options['thermo_data'] = pyc.species_data.janaf
FUEL_TYPE = 'Jet-A(g)'


#Add subsystems to build the engine deck:
self.add_subsystem('fc', pyc.FlightConditions())
self.add_subsystem('inlet', pyc.Inlet())
# Note variable promotion for the fan --

# Note variable promotion for the fan --
# the LP spool speed and the fan speed are INPUTS that are promoted:
# Note here that promotion aliases are used. Here Nmech is being aliased to LP_Nmech
# check out: http://openmdao.org/twodocs/versions/latest/features/core_features/grouping_components/add_subsystem.html?highlight=alias
# check out: https://openmdao.org/newdocs/versions/latest/features/core_features/working_with_groups/add_subsystem.html?highlight=alias
self.add_subsystem('fan', pyc.Compressor(map_data=pyc.FanMap,
bleed_names=[], map_extrap=True), promotes_inputs=[('Nmech','LP_Nmech')])
self.add_subsystem('splitter', pyc.Splitter())
Expand All @@ -63,22 +63,22 @@ def setup(self):
self.add_subsystem('byp_bld', pyc.BleedOut(bleed_names=['bypBld']))
self.add_subsystem('duct15', pyc.Duct())
self.add_subsystem('byp_nozz', pyc.Nozzle(nozzType='CV', lossCoef='Cv'))

#Create shaft instances. Note that LP shaft has 3 ports! => no gearbox
self.add_subsystem('lp_shaft', pyc.Shaft(num_ports=3),promotes_inputs=[('Nmech','LP_Nmech')])
self.add_subsystem('hp_shaft', pyc.Shaft(num_ports=2),promotes_inputs=[('Nmech','HP_Nmech')])
self.add_subsystem('perf', pyc.Performance(num_nozzles=2, num_burners=1))

# Now use the explicit connect method to make connections -- connect(<from>, <to>)

#Connect the inputs to perf group
self.connect('inlet.Fl_O:tot:P', 'perf.Pt2')
self.connect('hpc.Fl_O:tot:P', 'perf.Pt3')
self.connect('burner.Wfuel', 'perf.Wfuel_0')
self.connect('inlet.F_ram', 'perf.ram_drag')
self.connect('core_nozz.Fg', 'perf.Fg_0')
self.connect('byp_nozz.Fg', 'perf.Fg_1')

#LP-shaft connections
self.connect('fan.trq', 'lp_shaft.trq_0')
self.connect('lpc.trq', 'lp_shaft.trq_1')
Expand All @@ -89,7 +89,7 @@ def setup(self):
#Ideally expanding flow by conneting flight condition static pressure to nozzle exhaust pressure
self.connect('fc.Fl_O:stat:P', 'core_nozz.Ps_exhaust')
self.connect('fc.Fl_O:stat:P', 'byp_nozz.Ps_exhaust')

#Create a balance component
# Balances can be a bit confusing, here's some explanation -
# State Variables:
Expand All @@ -102,7 +102,7 @@ def setup(self):
# (lpt_PR) LPT press ratio to balance shaft power on the low spool
# (hpt_PR) HPT press ratio to balance shaft power on the high spool
# Ref: look at the XDSM diagrams in the pyCycle paper and this:
# http://openmdao.org/twodocs/versions/latest/features/building_blocks/components/balance_comp.html
# http://openmdao.org/newdocs/versions/latest/features/building_blocks/components/balance_comp.html

balance = self.add_subsystem('balance', om.BalanceComp())
if design:
Expand All @@ -116,7 +116,7 @@ def setup(self):
self.connect('balance.FAR', 'burner.Fl_I:FAR')
self.connect('burner.Fl_O:tot:T', 'balance.lhs:FAR')
self.promotes('balance', inputs=[('rhs:FAR', 'T4_MAX')])

# Note that for the following two balances the mult val is set to -1 so that the NET torque is zero
balance.add_balance('lpt_PR', val=1.5, lower=1.001, upper=8,
eq_units='hp', use_mult=True, mult_val=-1)
Expand All @@ -131,11 +131,11 @@ def setup(self):
self.connect('hp_shaft.pwr_out_real', 'balance.rhs:hpt_PR')

else:

#In OFF-DESIGN mode we need to redefine the balances:
# State Variables:
# (W) Inlet mass flow rate to balance core flow area
# LHS: core_nozz.Throat:stat:area == Area from DESIGN calculation
# LHS: core_nozz.Throat:stat:area == Area from DESIGN calculation
#
# (FAR) Fuel-air ratio to balance Thrust req.
# LHS: perf.Fn == RHS: Thrust requirement (set when TF is instantiated)
Expand All @@ -146,13 +146,13 @@ def setup(self):
# (lp_Nmech) LP spool speed to balance shaft power on the low spool
# (hp_Nmech) HP spool speed to balance shaft power on the high spool

if self.options['throttle_mode'] == 'T4':
if self.options['throttle_mode'] == 'T4':
balance.add_balance('FAR', val=0.017, lower=1e-4, eq_units='degR')
self.connect('balance.FAR', 'burner.Fl_I:FAR')
self.connect('burner.Fl_O:tot:T', 'balance.lhs:FAR')
self.promotes('balance', inputs=[('rhs:FAR', 'T4_MAX')])

elif self.options['throttle_mode'] == 'percent_thrust':
elif self.options['throttle_mode'] == 'percent_thrust':
balance.add_balance('FAR', val=0.017, lower=1e-4, eq_units='lbf', use_mult=True)
self.connect('balance.FAR', 'burner.Fl_I:FAR')
self.connect('perf.Fn', 'balance.rhs:FAR')
Expand All @@ -177,12 +177,12 @@ def setup(self):
self.connect('balance.hp_Nmech', 'HP_Nmech')
self.connect('hp_shaft.pwr_in_real', 'balance.lhs:hp_Nmech')
self.connect('hp_shaft.pwr_out_real', 'balance.rhs:hp_Nmech')

# Specify the order in which the subsystems are executed:

# self.set_order(['balance', 'fc', 'inlet', 'fan', 'splitter', 'duct4', 'lpc', 'duct6', 'hpc', 'bld3', 'burner', 'hpt', 'duct11',
# 'lpt', 'duct13', 'core_nozz', 'byp_bld', 'duct15', 'byp_nozz', 'lp_shaft', 'hp_shaft', 'perf'])

# Set up all the flow connections:
self.pyc_connect_flow('fc.Fl_O', 'inlet.Fl_I')
self.pyc_connect_flow('inlet.Fl_O', 'fan.Fl_I')
Expand All @@ -207,13 +207,13 @@ def setup(self):
self.pyc_connect_flow('hpc.cool2', 'lpt.cool2', connect_stat=False)
self.pyc_connect_flow('bld3.cool3', 'hpt.cool3', connect_stat=False)
self.pyc_connect_flow('bld3.cool4', 'hpt.cool4', connect_stat=False)

#Specify solver settings:
newton = self.nonlinear_solver = om.NewtonSolver()
newton.options['atol'] = 1e-8

# set this very small, so it never activates and we rely on atol
newton.options['rtol'] = 1e-99
newton.options['rtol'] = 1e-99
newton.options['iprint'] = 2
newton.options['maxiter'] = 50
newton.options['solve_subsystems'] = True
Expand Down Expand Up @@ -318,7 +318,7 @@ def setup(self):
self.set_input_defaults('DESIGN.HP_Nmech', 14705.7, units='rpm')

# --- Set up bleed values -----

self.pyc_add_cycle_param('inlet.ram_recovery', 0.9990)
self.pyc_add_cycle_param('duct4.dPqP', 0.0048)
self.pyc_add_cycle_param('duct6.dPqP', 0.0101)
Expand Down Expand Up @@ -346,7 +346,7 @@ def setup(self):
self.pyc_add_cycle_param('lpt.cool2:frac_P', 0.0)
self.pyc_add_cycle_param('hp_shaft.HPX', 250.0, units='hp')

self.od_pts = ['OD_full_pwr', 'OD_part_pwr']
self.od_pts = ['OD_full_pwr', 'OD_part_pwr']

self.od_MNs = [0.8, 0.8]
self.od_alts = [35000.0, 35000.0]
Expand Down Expand Up @@ -394,20 +394,20 @@ def setup(self):

prob.set_val('DESIGN.hpc.PR', 9.369)
prob.set_val('DESIGN.hpc.eff', 0.8707)

prob.set_val('DESIGN.hpt.eff', 0.8888)
prob.set_val('DESIGN.lpt.eff', 0.8996)

prob.set_val('DESIGN.fc.alt', 35000., units='ft')
prob.set_val('DESIGN.fc.MN', 0.8)

prob.set_val('DESIGN.T4_MAX', 2857, units='degR')
prob.set_val('DESIGN.Fn_DES', 5900.0, units='lbf')
prob.set_val('DESIGN.Fn_DES', 5900.0, units='lbf')

prob.set_val('OD_full_pwr.T4_MAX', 2857, units='degR')
prob.set_val('OD_part_pwr.PC', 0.8)


# Set initial guesses for balances
prob['DESIGN.balance.FAR'] = 0.025
prob['DESIGN.balance.W'] = 100.
Expand All @@ -416,15 +416,15 @@ def setup(self):
prob['DESIGN.fc.balance.Pt'] = 5.2
prob['DESIGN.fc.balance.Tt'] = 440.0


for pt in ['OD_full_pwr', 'OD_part_pwr']:

# initial guesses
prob[pt+'.balance.FAR'] = 0.02467
prob[pt+'.balance.W'] = 300
prob[pt+'.balance.BPR'] = 5.105
prob[pt+'.balance.lp_Nmech'] = 5000
prob[pt+'.balance.hp_Nmech'] = 15000
prob[pt+'.balance.lp_Nmech'] = 5000
prob[pt+'.balance.hp_Nmech'] = 15000
prob[pt+'.hpt.PR'] = 3.
prob[pt+'.lpt.PR'] = 4.
prob[pt+'.fan.map.RlineMap'] = 2.0
Expand All @@ -437,43 +437,43 @@ def setup(self):
prob.set_solver_print(level=2, depth=1)

flight_env = [(0.8, 35000), (0.7, 35000), (0.55, 35000), (0.46, 35000), (0.4, 35000),
(0.4, 20000), (0.6, 20000), (0.8, 20000),
(0.4, 20000), (0.6, 20000), (0.8, 20000),
(0.8, 10000), (0.6, 10000), (0.4, 10000), (0.2, 10000), (0.001, 10000),
(.001, 1000), (0.2, 1000), (0.4, 1000), (0.6, 1000),
(0.6, 0), (0.4, 0), (0.2, 0), (0.001, 0)]

viewer_file = open('hbtf_view.out', 'w')
first_pass = True
for MN, alt in flight_env:
for MN, alt in flight_env:

# NOTE: You never change the MN,alt for the
# NOTE: You never change the MN,alt for the
# design point because that is a fixed reference condition.

print('***'*10)
print(f'* MN: {MN}, alt: {alt}')
print('***'*10)
prob['OD_full_pwr.fc.MN'] = MN
prob['OD_full_pwr.fc.alt'] = alt

prob['OD_part_pwr.fc.MN'] = MN
prob['OD_part_pwr.fc.alt'] = alt

for PC in [1, 0.9, 0.8, .7]:
for PC in [1, 0.9, 0.8, .7]:
print(f'## PC = {PC}')
prob['OD_part_pwr.PC'] = PC
prob.run_model()

if first_pass:
if first_pass:
viewer(prob, 'DESIGN', file=viewer_file)
first_pass = False
first_pass = False
viewer(prob, 'OD_part_pwr', file=viewer_file)

# run throttle back up to full power
for PC in [1, 0.85]:
for PC in [1, 0.85]:
prob['OD_part_pwr.PC'] = PC
prob.run_model()



print()
print("Run time", time.time() - st)
2 changes: 1 addition & 1 deletion pycycle/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '4.2.1'
__version__ = '4.3.0'
33 changes: 12 additions & 21 deletions pycycle/docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ pyCycle is a open-source thermodynamic cycle analysis tool designed integrating
The pyCycle project implements 1D thermodynamic cycle analysis equations similar to the NPSS code and has been validated against this tool. While the thermodynamic equations are similar to NPSS and other available cycle analysis tools, pyCycle is focused on applying advanced methods for supporting gradient-based optimization through the implementation of analytic derivatives. This feature enables efficient exploration of large design spaces when pyCycle is coupled with other disciplinary analysis tools.


.. warning::
.. warning::

pyCycle is built on top of OpenMDAO and relies extensively its linear and nonlinear solver library.
pyCycle is built on top of OpenMDAO and relies extensively its linear and nonlinear solver library.
pyCycle was also designed with a user interface that is very similar to the NPSS cycle modeling library.
If you're not comfortable with either OpenMDAO or NPSS, then you may find this library difficult to understand and use.
If you're not comfortable with either OpenMDAO or NPSS, then you may find this library difficult to understand and use.

Installation
************
Expand All @@ -24,13 +24,13 @@ Installation of pyCycle requires two pieces of software. First, if you do not h
pip install pycycle


.. _OpenMDAO Getting Started: http://openmdao.org/twodocs/versions/latest/getting_started/index.html
.. _OpenMDAO Getting Started: https://openmdao.org/newdocs/versions/latest/getting_started/getting_started.html

Tutorials
**********

This section provides several tutorials showing how to build thermodynamic cycle models in pyCycle.
For users unfamiliar with OpenMDAO, a review of the OpenMDAO User Guide is highly recommended before completing the pyCycle tutorials.
This section provides several tutorials showing how to build thermodynamic cycle models in pyCycle.
For users unfamiliar with OpenMDAO, a review of the OpenMDAO User Guide is highly recommended before completing the pyCycle tutorials.
The tutorials in this section will demonstrate how models are constructed and executed in pyCycle, starting with a simple turboject then moving to a more complicated turbofan model.
Additional models showing more features available with pyCycle are provided in the Examples section.

Expand All @@ -40,14 +40,14 @@ Additional models showing more features available with pyCycle are provided in t
:name: tutorials

tutorials/index.rst

Reference Guide
****************

The reference guide intended for users looking for explanation of a particular feature in detail or documentation of the arguments/options/settings for a specific cycle element, map or viewer.

.. toctree::
:maxdepth: 1
.. toctree::
:maxdepth: 1
:name: reference_guide

reference_guide/elements/index.rst
Expand All @@ -56,21 +56,12 @@ The reference guide intended for users looking for explanation of a particular f



Examples
Examples
********
The examples in this section provide a more comprehensive demsonstration of the features of pyCycle and advanced modeling methods.

.. toctree::
:maxdepth: 1
.. toctree::
:maxdepth: 1
:name: examples

examples/index.rst









Loading
Loading