Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

513

514

515

516

517

518

519

520

521

522

523

524

525

526

527

528

529

530

531

532

533

534

535

536

537

538

539

540

541

542

543

544

545

546

547

548

549

550

551

552

553

554

555

556

557

558

559

560

561

562

563

564

565

566

567

568

569

570

571

572

573

574

575

576

577

578

579

580

581

582

583

584

585

586

587

588

589

590

591

592

593

594

595

596

597

598

599

600

601

602

603

604

605

606

607

608

609

610

611

612

613

614

615

616

617

618

619

620

621

622

623

624

625

626

627

628

629

630

631

632

633

634

635

636

637

638

639

640

641

642

643

644

645

646

647

648

649

650

651

652

653

654

655

656

657

658

659

660

661

662

663

664

665

666

667

668

669

670

671

672

673

674

675

676

677

678

679

680

681

682

683

684

685

686

687

688

689

690

691

692

693

694

695

696

697

698

699

700

701

702

703

704

705

706

707

708

709

710

711

712

713

714

715

716

717

718

719

720

721

722

from math import pi 

import os 

from rocketisp.model_summ import ModelSummary 

from rocketisp.parse_docstring import get_desc_and_units 

 

 

if 'READTHEDOCS' not in os.environ: 

from rocketcea.cea_obj import CEA_Obj 

from rocketcea.separated_Cf import sepNozzleCf 

else: 

from rocketisp.mock.cea_obj import CEA_Obj 

from rocketisp.mock.separated_Cf import sepNozzleCf 

 

from rocketisp.efficiency.calc_noz_kinetics import calc_IspODK 

from rocketisp.efficiencies import Efficiencies 

from rocketisp.geometry import Geometry 

 

def shapeFactor( WentrOvWcool ): 

'''Subsonic Shape Factor from COMBUSTION EFFECTS ON FILM COOLING manual Figure 5, page 88 

https://ntrs.nasa.gov/citations/19770014416 

"Mixing Layer Profile Shape Factor Correlations"''' 

if WentrOvWcool > 1.4: 

return 1.0 / 1.32 

elif WentrOvWcool < 0.06: 

# from the definintion of effectiveness and the fact that effectiveness=1 

return 1.0 / (1.0 + WentrOvWcool) 

else: 

# curve fit of COMBUSTION EFFECTS ON FILM COOLING manual figure 5 subsonic curve 

return (0.962+(0.299*WentrOvWcool))/(1+(0.67*WentrOvWcool)+(-0.06*WentrOvWcool**2)) 

 

def solve_At_split( MRc, MRb, ffc, cstar_c, cstar_b ): 

""" 

Given MRcore, MRbarrier, fracFFC, cstar_c and cstar_b, solve the throat area split 

between the core and barrier. 

""" 

MReng = MRc * (1.0 - ffc) 

fAtc_min = 0.0 

fAtc_max = 1.0 

ccm = cstar_c * (MRc + 1.0) 

cbm = cstar_b * (MRb + 1.0) 

for _ in range(40): 

fAtc = (fAtc_min + fAtc_max)/2.0 

 

Atc = fAtc 

Atb = 1.0 - fAtc 

mre = (Atc*MRc/ccm + Atb*MRb/cbm) / (Atc/ccm + Atb/cbm) 

 

if mre < MReng: 

fAtc_min = fAtc 

else: 

fAtc_max = fAtc 

return (fAtc_min + fAtc_max)/2.0 

 

 

class BarrierStream: 

""" 

A BarrierStream is typically used to evaluate fuel film cooling,  

see: https://ntrs.nasa.gov/citations/19770014416 

COMBUSTION EFFECTS ON FILM COOLING, 24 Feb 1977 by Aerojet Liquid Rocket Co. 

 

:param coreObj: core stream tube object, CoreStream 

:param pcentFFC: percent fuel film cooling ( FFC flowrate / total fuel flowrate) 

:param ko: entrainment constant (typical value is 0.035, range from 0.03 to 0.06) 

:type coreObj: CoreStream 

:type pcentFFC: float 

:type ko: float 

:return: BarrierStream object 

:rtype: BarrierStream 

 

:ivar MRbarrier: barrier mixture ratio 

:ivar MRwall: mixture ratio at wall 

:ivar Twallgas: degR, temperature of gas at wall 

:ivar TcODE_b: degR, average ideal ODE temperature of barrier gas 

:ivar WentrOvWcool: ratio of entrained flow rate to FFC flow rate 

:ivar IspDel_b: sec, delivered vacuum barrier Isp 

:ivar IspODF_b: sec, ideal frozen barrier Isp 

:ivar IspODK_b: sec, vacuum kinetic Isp of barrier 

:ivar fracKin_b: fraction of kinetic completion in barrier 

:ivar IspODE_b: sec. ideal equilibrium barrier Isp 

:ivar cstarERE_b: ft/s, delivered cstar 

:ivar cstarODE_b: ft/s, ideal equilibrium cstar  

""" 

def __init__(self, coreObj, pcentFFC=10.0, ko=0.035): 

""" 

A BarrierStream, see: https://ntrs.nasa.gov/citations/19770014416 

COMBUSTION EFFECTS ON FILM COOLING, 24 Feb 1977 by Aerojet Liquid Rocket Co. 

 

""" 

self.coreObj = coreObj 

self.geomObj = coreObj.geomObj 

self.ceaObj = coreObj.ceaObj 

self.pcentFFC = pcentFFC 

self.ko = ko 

self.warningL = [] # list of any evaluate warnings 

 

self.evaluate() 

 

# get input descriptions and units from doc string 

self.inp_descD, self.inp_unitsD, self.is_inputD = get_desc_and_units( self.__doc__ ) 

 

def __call__(self, name): 

return getattr(self, name ) # let it raise exception if no name attr. 

 

def evaluate(self): 

""" 

Estimate entrained core flow into film cooled stream tube and calculate 

performance of barrier stream tube. 

""" 

 

self.warningL = [] 

 

# figure out entrainment fraction and MRbarrier based on geometry and pcentFFC 

LprimeOvRcham = self.geomObj.Lcham / self.geomObj.Rinj 

 

# an approximation for equation 27 in COMBUSTION EFFECTS ON FILM COOLING, page 20 

fracEntr = 2.0*(LprimeOvRcham*self.ko) - (LprimeOvRcham*self.ko)**2 

 

fracFFC = self.pcentFFC / 100.0 

MReng = self.coreObj.MRcore * (1.0 - fracFFC) 

 

# use a ref. flow rate of 1.0... will generate ratios from ref flow rate 

wdotCoreInj = 1.0 # a ref flow rate 

wdotFuelCoreInj = wdotCoreInj / (1.0 + self.coreObj.MRcore) 

wdotOxCoreInj = wdotCoreInj - wdotFuelCoreInj 

wdotFuelTot = MReng * wdotOxCoreInj 

 

wdotFFC = fracFFC * wdotFuelTot # relative to ref wdotCoreInj 

wdotEntr = fracEntr * wdotCoreInj 

 

self.WentrOvWcool = wdotEntr / wdotFFC 

 

# shape factor comes from a curve fit of COMBUSTION EFFECTS ON FILM COOLING Figure 5, page 88 

SFact = shapeFactor(self.WentrOvWcool) 

 

# effectiveness come from equation 17 in COMBUSTION EFFECTS ON FILM COOLING, page 15 

self.effnessFC = 1.0/(SFact * (1.0 + self.WentrOvWcool)) 

 

#wdotFuelEntr = wdotFuelTot * (fracEntr - (fracEntr * fracFFC)) 

wdotFuelEntr = wdotFuelCoreInj * fracEntr 

wdotOxEntr = wdotOxCoreInj * fracEntr 

 

self.MRbarrier = wdotOxEntr / (wdotFuelEntr + wdotFFC) 

 

#print('self.WentrOvWcool=%g'%self.WentrOvWcool,' fracEntr=%g'%fracEntr,  

# ' MRcore=%g'%self.coreObj.MRcore, ' effnessFC=%g'%self.effnessFC,  

# ' MRbarrier=%g'%self.MRbarrier, ' MReng=%g'%MReng) 

 

 

# using eqn (2) on page 7 (pdf 21) of COMBUSTION EFFECTS ON FILM COOLING 

# film cooling effectiveness is always equal to the 

# mass fraction of the injected film coolant gas within the gas mixture directly 

# adjacent to the wall. 

massfracOxWall = (1.0 - self.effnessFC) * self.MRbarrier / (1.0 + self.MRbarrier) 

self.MRwall = massfracOxWall / (1.0 - massfracOxWall) 

 

self.Twallgas = self.ceaObj.get_Tcomb( Pc=self.coreObj.Pc, MR=self.MRwall) 

 

# ........... calc ideal performance parameters 

self.IspODE_b, self.cstarODE_b, self.TcODE_b, self.MWchm_b, self.gammaChm_b = \ 

self.ceaObj.get_IvacCstrTc_ChmMwGam( Pc=self.coreObj.Pc, MR=self.MRbarrier, 

eps=self.geomObj.eps) 

 

self.cstarODE_b *= self.coreObj.adjCstarODE 

self.IspODE_b *= self.coreObj.adjIspIdeal 

 

self.IspODF_b,_,_ = self.ceaObj.getFrozen_IvacCstrTc( Pc=self.coreObj.Pc, MR=self.MRbarrier, 

eps=self.geomObj.eps) 

self.IspODF_b *= self.coreObj.adjIspIdeal 

 

if self.IspODF_b < 10.0: # there's an error in frozen low MR CEA, so estimate from core  

self.warningL.append( 'WARNING... CEA failed frozen Isp for MR=%g'%self.MRbarrier ) 

 

self.IspODF_b = self.IspODE_b * (self.coreObj.IspODF / self.coreObj.IspODE) 

 

self.IspODK_b = self.IspODF_b + self.coreObj.fracKin*(self.IspODE_b - self.IspODF_b) 

self.warningL.append( ' Estimated IspODF_b = %g sec'%self.IspODF_b ) 

 

else: 

# use user effKin to set IspODK 

self.IspODK_b = calc_IspODK(self.ceaObj, Pc=self.coreObj.Pc, eps=self.geomObj.eps, 

Rthrt=self.geomObj.Rthrt, 

pcentBell=self.geomObj.pcentBell, 

MR=self.MRbarrier) 

self.IspODK_b *= self.coreObj.adjIspIdeal 

 

self.fracKin_b = (self.IspODK_b - self.IspODF_b) / (self.IspODE_b - self.IspODF_b) 

 

self.effKin_b = self.IspODK_b / self.IspODE_b 

 

# ........ make final summary efficiencies 

effObj = self.coreObj.effObj 

 

if effObj.effD['Noz'].is_const: 

self.effNoz_b = self.effKin_b * effObj('Noz') 

else: 

self.effNoz_b = self.effKin_b * effObj('Div') * effObj('BL') * effObj('TP') 

 

if effObj.effD['ERE'].is_const: 

self.effERE_b = effObj('ERE') 

else: 

self.effERE_b = effObj('Vap') * effObj('Mix') * effObj('Em') * effObj('HL') 

 

self.effIsp_b = self.effNoz_b * self.effERE_b 

self.IspDel_b = self.effIsp_b * self.IspODE_b 

self.cstarERE_b = self.cstarODE_b * self.effERE_b 

 

 

def summ_print(self): 

""" 

print to standard output, the current state of BarrierStream instance. 

""" 

print( self.get_summ_str() ) 

 

def get_summ_str(self, alpha_ordered=True, numbered=False, add_trailer=True, 

fillchar='.', max_banner=76, intro_str=''): 

 

""" 

return string of the current state of BarrierStream instance. 

""" 

 

M = self.get_model_summ_obj() 

return M.summ_str(alpha_ordered=alpha_ordered, numbered=numbered, 

add_trailer=add_trailer, fillchar=fillchar, 

max_banner=max_banner, intro_str=intro_str) 

 

def get_html_str(self, alpha_ordered=True, numbered=False, intro_str=''): 

M = self.get_model_summ_obj() 

return M.html_table_str( alpha_ordered=alpha_ordered, numbered=numbered, intro_str=intro_str) 

 

def get_model_summ_obj(self): 

""" 

return ModelSummary object for current state of BarrierStream instance. 

""" 

 

M = ModelSummary( 'Barrier Stream Tube' ) 

M.add_alt_units('ft/s', 'm/s') 

M.add_alt_units('sec', ['N-sec/kg', 'km/sec']) 

M.add_alt_units('degR', ['degK','degC','degF']) 

 

# function to add parameters from __doc__ string to ModelSummary 

def add_param( name, desc='', fmt='', units='', value=None): 

 

if name in self.inp_unitsD: 

units = self.inp_unitsD[name] 

 

if desc=='' and name in self.inp_descD: 

desc = self.inp_descD[name] 

 

if value is None: 

value = getattr( self, name ) 

 

if self.is_inputD.get(name, False): 

M.add_inp_param( name, value, units, desc, fmt=fmt) 

else: 

M.add_out_param( name, value, units, desc, fmt=fmt) 

 

for name in self.is_inputD.keys(): 

if name not in ['coreObj']: 

add_param( name ) 

''' 

add_param('MRbarrier', desc='barrier mixture ratio') 

add_param('MRwall', desc='mixture ratio at wall') 

add_param('Twallgas', units='degR', desc='temperature of gas at wall', fmt='%.0f') 

add_param('TcODE_b', units='degR', desc='average ideal ODE temperature of barrier gas', fmt='%.0f') 

#add_param('effnessFC', desc='effectiveness from equation 17 in COMBUSTION EFFECTS ON FILM COOLING, page 15') 

add_param('WentrOvWcool', desc='ratio of entrained flow rate to FFC flow rate') 

add_param('IspDel_b', units='sec', desc='delivered vacuum barrier Isp', fmt='%.1f') 

add_param('IspODF_b', units='sec', desc='ideal frozen barrier Isp', fmt='%.1f') 

add_param('IspODK_b', units='sec', desc='vacuum kinetic Isp of barrier', fmt='%.1f') 

add_param('fracKin_b', desc='fraction of kinetic completion in barrier') 

add_param('IspODE_b', units='sec', desc='ideal equilibrium barrier Isp', fmt='%.1f') 

add_param('cstarERE_b', units='ft/s', desc='delivered cstar', fmt='%.1f') 

add_param('cstarODE_b', units='ft/s', desc='ideal equilibrium cstar', fmt='%.1f') 

''' 

return M 

 

class CoreStream: 

""" 

Core stream tube of liquid bipropellant thruster. 

 

:param geomObj: Geometry that describes thruster 

:param effObj: Efficiencies object to hold individual efficiencies 

:param oxName: name of oxidizer (e.g. N2O4, LOX) 

:param fuelName: name of fuel (e.g. MMH, LH2) 

:param MRcore: mixture ratio of core flow (ox flow rate / fuel flow rate) 

:param Pc: psia, chamber pressure 

:param CdThroat: Cd of throat (RocketThruster object may override) 

:param Pamb: psia, ambient pressure (for example sea level is 14.7 psia) 

:param adjCstarODE: multiplier on NASA CEA code value of cstar ODE (default is 1.0) 

:param adjIspIdeal: multiplier on NASA CEA code value of Isp ODE (default is 1.0) 

:param pcentFFC: percent fuel film cooling (if > 0 then add BarrierStream) 

:param ko: entrainment constant (passed to BarrierStream object, range from 0.03 to 0.06) 

:param ignore_noz_sep: flag to force nozzle flow separation to be ignored (USE WITH CAUTION) 

:type geomObj: Geometry 

:type effObj: Efficiencies 

:type oxName: str 

:type fuelName: str 

:type MRcore: float 

:type Pc: float 

:type CdThroat: float 

:type Pamb: float 

:type adjCstarODE: float 

:type adjIspIdeal: float 

:type pcentFFC: float 

:type ko: float 

:type ignore_noz_sep: bool 

:return: CoreStream object 

:rtype: CoreStream  

 

:ivar FvacTotal: lbf, total vacuum thrust 

:ivar FvacCore: lbf, vacuum thrust due to core stream tube 

:ivar MRthruster: total thruster mixture ratio') 

:ivar IspDel: sec, <=== thruster delivered vacuum Isp ===> 

:ivar Pexit: psia, nozzle exit pressure 

:ivar IspDel_core: sec, delivered Isp of core stream tube 

:ivar IspODF: sec, core frozen Isp 

:ivar IspODK: sec, core one dimensional kinetic Isp 

:ivar IspODE: sec, core one dimensional equilibrium Isp 

:ivar cstarERE: ft/s, delivered core cstar 

:ivar cstarODE: ft/s, core ideal cstar 

:ivar CfVacIdeal: ideal vacuum thrust coefficient 

:ivar CfVacDel: delivered vacuum thrust coefficient 

:ivar CfAmbDel: delivered ambient thrust coefficient 

:ivar wdotTot: lbm/s, total propellant flow rate (ox+fuel) 

:ivar wdotOx: lbm/s, total oxidizer flow rate 

:ivar wdotFl: lbm/s, total fuel flow rate 

:ivar TcODE: degR, ideal core gas temperature 

:ivar MWchm: g/gmole, core gas molecular weight 

:ivar gammaChm: core gas ratio of specific heats (Cp/Cv) 

 

""" 

 

def __init__(self, geomObj=Geometry(), effObj=Efficiencies(), #ERE=0.98, Noz=0.97),  

oxName='N2O4', fuelName='MMH', MRcore=1.9, 

Pc=500, CdThroat=0.995, Pamb=0.0, adjCstarODE=1.0, adjIspIdeal=1.0, 

pcentFFC=0.0, ko=0.035, ignore_noz_sep=False): 

 

self.geomObj = geomObj 

self.effObj = effObj 

self.oxName = oxName 

self.fuelName = fuelName 

self.MRcore = MRcore 

self.Pc = Pc 

self.Pamb = Pamb # ambient pressure 

self.noz_mode = '' 

self.CdThroat = CdThroat 

 

self.CdThroat_method = 'default' 

self.ignore_noz_sep = ignore_noz_sep # ignore any nozzle separation 

 

self.adjCstarODE = adjCstarODE # may want to adjust ODE cstar value 

self.adjIspIdeal = adjIspIdeal # may want to adjust ODE and ODF Isp values 

 

# make CEA object 

self.ceaObj = CEA_Obj(oxName=oxName, fuelName=fuelName) 

 

# ... if pcentFFC > 0.0, then there's barrier cooling 

if pcentFFC > 0.0: 

self.add_barrier = True 

else: 

self.add_barrier = False 

 

# barrier might need some performance parameters from CoreStream 

self.calc_cea_perf_params() 

 

if self.add_barrier: 

self.barrierObj = BarrierStream(self, pcentFFC=pcentFFC, ko=ko) 

else: 

self.barrierObj = None 

 

self.evaluate() 

 

# get input descriptions and units from doc string 

self.inp_descD, self.inp_unitsD, self.is_inputD = get_desc_and_units( self.__doc__ ) 

 

def __call__(self, name): 

return getattr(self, name ) # let it raise exception if no name attr. 

 

def reset_CdThroat(self, value, method_name='RocketIsp', re_evaluate=True): 

""" 

reset the value of CdThroat 

If re_evaluate is True, then call self.evaluate() after resetting the efficiency. 

""" 

self.CdThroat = value 

self.CdThroat_method = method_name 

 

if re_evaluate: 

self.evaluate() 

 

def reset_attr(self, name, value, re_evaluate=True): 

""" 

reset the value of any existing attribute of CoreStream instance. 

If re_evaluate is True, then call self.evaluate() after resetting the value of the attribute. 

""" 

if hasattr( self, name ): 

setattr( self, name, value ) 

else: 

raise Exception('Attempting to set un-authorized CoreStream attribute named "%s"'%name ) 

 

if name in ['oxName','fuelName']: 

# make CEA object 

self.ceaObj = CEA_Obj(oxName=self.oxName, fuelName=self.fuelName) 

 

 

if re_evaluate: 

self.evaluate() 

 

def calc_cea_perf_params(self): 

"""Calc basic Isp values from CEA and calc implied IspODK from current effKin value.""" 

 

# calc ideal CEA performance parameters 

self.IspODE, self.cstarODE, self.TcODE, self.MWchm, self.gammaChm = \ 

self.ceaObj.get_IvacCstrTc_ChmMwGam( Pc=self.Pc, MR=self.MRcore, eps=self.geomObj.eps) 

 

self.cstarODE *= self.adjCstarODE 

self.IspODE *= self.adjIspIdeal 

 

self.IspODF,_,_ = self.ceaObj.getFrozen_IvacCstrTc( Pc=self.Pc, MR=self.MRcore, 

eps=self.geomObj.eps) 

self.IspODF *= self.adjIspIdeal 

 

# use user effKin to set IspODK (or most recent update) 

self.IspODK = self.IspODE * self.effObj('Kin') 

#self.IspODK = calc_IspODK(self.ceaObj, Pc=self.Pc, eps=self.geomObj.eps,  

# Rthrt=self.geomObj.Rthrt,  

# pcentBell=self.geomObj.pcentBell,  

# MR=self.MRcore) 

 

# fraction of equilibrium kinetics obtained 

self.fracKin = (self.IspODK - self.IspODF) / (self.IspODE - self.IspODF) 

 

 

self.Pexit = self.Pc / self.ceaObj.get_PcOvPe( Pc=self.Pc, MR=self.MRcore, eps=self.geomObj.eps) 

 

self.CfVacIdeal = 32.174 * self.IspODE / self.cstarODE 

 

 

def evaluate(self): 

""" 

Assume that all efficiencies have been set, either by original user value 

or an update by an efficiency model. 

""" 

self.effObj.evaluate() 

self.calc_cea_perf_params() 

 

# make final summary efficiencies 

effNoz = self.effObj('Noz') 

 

# want a Core-Only ERE in case a barrier calc is done 

effERE_core = self.effObj('ERE') 

if not self.add_barrier: # if no barrier, user may have input FFC 

effERE_core = effERE_core * self.effObj('FFC') 

 

cstarERE_core = self.cstarODE * effERE_core 

 

effIsp_core = effNoz * effERE_core 

self.IspDel_core = effIsp_core * self.IspODE 

 

if self.add_barrier: 

self.barrierObj.evaluate() 

 

fAtc = solve_At_split( self.MRcore, self.barrierObj.MRbarrier, 

self.barrierObj.pcentFFC / 100.0, 

cstarERE_core, self.barrierObj.cstarERE_b ) 

 

self.frac_At_core = fAtc # core shares throat area with barrier stream 

 

self.frac_At_barrier = 1.0 - self.frac_At_core 

self.At_b = self.frac_At_barrier * self.geomObj.At 

 

self.wdotTot_b = self.Pc * self.At_b * self.CdThroat * 32.174 / self.barrierObj.cstarERE_b 

self.wdotOx_b = self.wdotTot_b * self.barrierObj.MRbarrier / (1.0 + self.barrierObj.MRbarrier) 

self.wdotFl_b = self.wdotTot_b - self.wdotOx_b 

 

self.FvacBarrier = self.wdotTot_b * self.barrierObj.IspDel_b 

self.MRthruster = self.MRcore * (1.0 - self.barrierObj.pcentFFC / 100.0) 

else: 

self.frac_At_core = 1.0 # core gets all of throat area if no barrier stream 

self.frac_At_barrier = 0.0 

self.FvacBarrier = 0.0 

self.MRthruster = self.MRcore 

 

self.wdotTot_b = 0.0 

self.wdotOx_b = 0.0 

self.wdotFl_b = 0.0 

 

 

self.Atcore = self.frac_At_core * self.geomObj.At 

 

self.wdotTot_c = self.Pc * self.Atcore * self.CdThroat * 32.174 / cstarERE_core 

self.wdotOx_c = self.wdotTot_c * self.MRcore / (1.0 + self.MRcore) 

self.wdotFl_c = self.wdotTot_c - self.wdotOx_c 

 

self.FvacCore = self.wdotTot_c * self.IspDel_core 

self.FvacTotal = self.FvacCore + self.FvacBarrier 

 

self.wdotTot = self.wdotTot_c + self.wdotTot_b 

self.wdotOx = self.wdotOx_c + self.wdotOx_b 

self.wdotFl = self.wdotFl_c + self.wdotFl_b 

 

if self.add_barrier: 

self.wdotFlFFC = (self.barrierObj.pcentFFC/100.0) * self.wdotFl 

self.wdotFl_cInit = self.wdotFl - self.wdotFlFFC 

self.wdotTot_cInit = self.wdotOx + self.wdotFl_cInit 

else: 

self.wdotFlFFC = 0.0 

self.wdotFl_cInit = self.wdotFl 

self.wdotTot_cInit = self.wdotTot 

 

self.IspDel = self.FvacTotal / self.wdotTot 

 

self.IspDelPulse = self.IspDel* self.effObj('Pulse') 

 

if self.add_barrier: # if barrier is analysed, assume it is in addition to user input effERE 

effFFC = self.IspDel / self.IspDel_core 

self.effObj.set_value('FFC', effFFC, value_src='barrier calc' ) 

 

self.cstarERE = self.cstarODE * self.effObj('ERE') 

#self.cstarDel = self.Pc * self.Atcore * self.CdThroat * 32.174 / self.wdotTot 

 

 

# do any nozzle ambient performance calcs here 

if self.Pamb < 0.000001: 

self.IspAmb = self.IspDel 

self.noz_mode = '(Pexit=%g psia)'%self.Pexit 

else: 

CfOvCfvacAtEsep, CfOvCfvac, Cfsep, CfiVac, CfiAmbSimple, CfVac, epsSep, self.Psep = \ 

sepNozzleCf(self.gammaChm, self.geomObj.eps, self.Pc, self.Pamb) 

#print('epsSep=%g, Psep=%g'%(epsSep, self.Psep)) 

#print('========= Pexit=%g'%self.Pexit, ' Psep=%g'%self.Psep, ' epsSep=%g'%epsSep) 

 

if self.Pexit > self.Psep or self.ignore_noz_sep: 

# if not separated, use theoretical equation for back-pressure correction 

self.IspAmb = self.IspDel - self.cstarERE * self.Pamb * self.geomObj.eps / self.Pc / 32.174 

#print('----------------> subtraction term =', self.cstarERE * self.Pamb * self.geomObj.eps / self.Pc / 32.174) 

else: 

# if separated, use Kalt and Badal estimate of ambient thrust coefficient 

# NOTE: there are better, more modern methods available 

IspODEepsSep, CstarODE, Tc = \ 

self.ceaObj.get_IvacCstrTc(Pc=self.Pc, MR=self.MRcore, eps=epsSep) 

 

effPamb = IspODEepsSep / self.IspODE 

#print('--------------> effPamb=%g'%effPamb, ' IspODEepsSep=%g'%IspODEepsSep, ' IspODE=%g'%self.IspODE) 

 

self.IspAmb = effPamb * self.IspDel 

 

#print('========= Pamb=%g'%self.Pamb, ' IspAmb=%g'%self.IspAmb) 

# figure out mode of nozzle operation 

if self.Pexit > self.Psep: 

if self.Pexit > self.Pamb + 0.05: 

self.noz_mode = 'UnderExpanded (Pexit=%g)'%self.Pexit 

elif self.Pexit < self.Pamb - 0.05: 

self.noz_mode = 'OverExpanded (Pexit=%g)'%self.Pexit 

else: 

self.noz_mode = 'Pexit=%g'%self.Pexit 

else: 

self.noz_mode = 'Separated (Psep=%g, epsSep=%g)'%(self.Psep,epsSep) 

 

self.Fambient = self.FvacTotal * self.IspAmb / self.IspDel 

 

self.CfVacDel = self.FvacTotal / (self.geomObj.At * self.Pc) # includes impact of CdThroat 

self.CfAmbDel = self.Fambient / (self.geomObj.At * self.Pc) # includes impact of CdThroat 

 

def summ_print(self): 

""" 

print to standard output, the current state of CoreStream instance. 

""" 

print( self.get_summ_str() ) 

 

def get_summ_str(self, alpha_ordered=True, numbered=False, add_trailer=True, 

fillchar='.', max_banner=76, intro_str=''): 

""" 

return string of the current state of CoreStream instance. 

""" 

 

M = self.get_model_summ_obj() 

 

Me = self.effObj.get_model_summ_obj() 

se = '\n' + Me.summ_str(alpha_ordered=False, fillchar=' ', assumptions_first=False) 

 

if self.add_barrier: 

Mb = self.barrierObj.get_model_summ_obj() 

sb = '\n' + Mb.summ_str(alpha_ordered=alpha_ordered, numbered=numbered, 

add_trailer=add_trailer, fillchar=fillchar, 

max_banner=max_banner, intro_str=intro_str) 

else: 

sb = '' 

 

return M.summ_str(alpha_ordered=alpha_ordered, numbered=numbered, 

add_trailer=add_trailer, fillchar=fillchar, 

max_banner=max_banner, intro_str=intro_str) + se + sb 

 

def get_html_str(self, alpha_ordered=True, numbered=False, intro_str=''): 

M = self.get_model_summ_obj() 

 

 

Me = self.effObj.get_model_summ_obj() 

se = '\n' + Me.html_table_str(alpha_ordered=False, assumptions_first=False) 

 

if self.add_barrier: 

Mb = self.barrierObj.get_model_summ_obj() 

sb = '\n' + Mb.html_table_str(alpha_ordered=alpha_ordered, numbered=numbered, 

intro_str=intro_str) 

else: 

sb = '' 

 

 

return M.html_table_str( alpha_ordered=alpha_ordered, numbered=numbered, intro_str=intro_str)\ 

+ se + sb 

 

def get_model_summ_obj(self): 

""" 

return ModelSummary object for current state of CoreStream instance. 

""" 

 

M = ModelSummary( '%s/%s Core Stream Tube'%(self.oxName, self.fuelName) ) 

M.add_alt_units('psia', ['MPa','atm','bar']) 

M.add_alt_units('lbf', 'N') 

M.add_alt_units('lbm/s', 'kg/s') 

M.add_alt_units('ft/s', 'm/s') 

M.add_alt_units('sec', ['N-sec/kg', 'km/sec']) 

M.add_alt_units('degR', ['degK','degC','degF']) 

 

M.add_param_fmt('Pexit', '%.4f') 

M.add_param_fmt('Pc', '%.1f') 

 

M.add_out_category( '' ) # show unlabeled category 1st 

 

 

def add_param( name, desc='', fmt='', units='', value=None, category=''): 

 

if name in self.inp_unitsD: 

units = self.inp_unitsD[name] 

 

if desc=='' and name in self.inp_descD: 

desc = self.inp_descD[name] 

 

if value is None: 

value = getattr( self, name ) 

 

if self.is_inputD.get(name, False): 

M.add_inp_param( name, value, units, desc, fmt=fmt) 

else: 

M.add_out_param( name, value, units, desc, fmt=fmt, category=category) 

 

for name in self.is_inputD.keys(): 

if name not in ['pcentFFC','ko', 'geomObj', 'effObj']: 

add_param( name ) 

 

# parameters that are NOT attributes OR are conditional 

if self.add_barrier: 

add_param('FvacBarrier', units='lbf', desc='vacuum thrust due to barrier stream tube') 

 

if self.Pamb > 14.5: 

add_param('Fambient', units='lbf', desc='total sea level thrust') 

add_param('IspAmb', units='sec', desc='delivered sea level Isp' ) 

M.add_out_comment('Fambient', '%s'%self.noz_mode ) 

M.add_out_comment('IspAmb', '%s'%self.noz_mode ) 

elif self.Pamb > 0.0: 

add_param('Fambient', units='lbf', desc='total ambient thrust') 

add_param('IspAmb', units='sec', desc='delivered ambient Isp' ) 

M.add_out_comment('Fambient', '%s'%self.noz_mode) 

M.add_out_comment('IspAmb', '%s'%self.noz_mode ) 

 

 

if self.effObj('Pulse') < 1.0: 

add_param('IspDelPulse', units='sec', desc='delivered pulsing Isp') 

 

 

if self.CdThroat_method != 'default': 

M.add_inp_comment('CdThroat', '(%s)'%self.CdThroat_method) 

 

 

if self.add_barrier: 

add_param('wdotFlFFC', units='lbm/s', desc='fuel film coolant flow rate injected at perimeter', 

category='At Injector Face') 

add_param('wdotFl_cInit', units='lbm/s', desc='initial core fuel flow rate (before any entrainment)', 

category='At Injector Face') 

add_param('wdotTot_cInit', units='lbm/s', desc='initial core total flow rate (before any entrainment)', 

category='At Injector Face') 

 

add_param('wdotTot_b', units='lbm/s', desc='total barrier propellant flow rate (includes entrained)', 

category='After Entrainment') 

add_param('wdotOx_b', units='lbm/s', desc='barrier oxidizer flow rate (all entrained)', 

category='After Entrainment') 

add_param('wdotFl_b', units='lbm/s', desc='barrier fuel flow rate (FFC + entrained)', 

category='After Entrainment') 

 

add_param('wdotTot_c', units='lbm/s', desc='total final core propellant flow rate (injected - entrained)', 

category='After Entrainment') 

add_param('wdotOx_c', units='lbm/s', desc='final core oxidizer flow rate (injected - entrained)', 

category='After Entrainment') 

add_param('wdotFl_c', units='lbm/s', desc='final core fuel flow rate (injected - entrained)', 

category='After Entrainment') 

 

 

#add_param('xxx', units='xxx', desc='xxx') 

 

return M 

 

 

if __name__ == '__main__': 

from rocketisp.geometry import Geometry 

from rocketisp.efficiencies import Efficiencies 

 

geomObj = Geometry(Rthrt=5.868/2, 

CR=2.5, eps=150, pcentBell=80, 

RupThroat=1.5, RdwnThroat=1.0, RchmConv=1.0, cham_conv_deg=30, 

LchmOvrDt=3.10, LchmMin=2.0, LchamberInp=16) 

 

effObj = Efficiencies() 

effObj.set_const('Mix', 0.997329) 

effObj.set_const('Em', 0.99644) 

effObj.set_const('Kin', 0.975011) 

effObj.set_const('BL', 0.9795) 

effObj.set_const('Div', 0.994775) 

 

core = CoreStream( geomObj, effObj, oxName='N2O4', fuelName='MMH', MRcore=1.85, 

Pc=150, CdThroat=0.995, Pamb=14.7, 

pcentFFC=14.0, ko=0.035) 

#core.reset_attr('Pc', 456) 

core.summ_print()