def minimizeUtilization(vessel, layerNumber, bounds, dropIndicies, puckProperties, burstPressure, targetFunction=getMaxFibreFailureByAngle, verbose=False): """Minimizes puck fibre failure criterion in a certain region of angles """ tol = 1e-2 args = [ vessel, layerNumber, puckProperties, burstPressure, dropIndicies, verbose ] if 0: popt = minimize_scalar( targetFunction, method='bounded', bounds=bounds, # bounds of the angle args=args, options={ "maxiter": 1000, 'disp': 1, "xatol": tol }) else: popt = differential_evolution(targetFunction, bounds=(bounds, ), args=[args], atol=tol * 10) if not popt.success: raise Tankoh2Error('Could not finde optimal solution') x, funVal, iterations = popt.x, popt.fun, popt.nfev if hasattr(x, '__iter__'): x = x[0] if targetFunction is getMaxFibreFailureByAngle: windLayer(vessel, layerNumber, x) else: windHoopLayer(vessel, layerNumber, x) return x, funVal, iterations
def optimizeAngle(vessel, targetPolarOpening, layerNumber, minAngle, verbose=False, targetFunction=getPolarOpeningDiffByAngle): """optimizes the angle of the actual layer to realize the desired polar opening :param vessel: vessel object :param targetPolarOpening: polar opening radius that should be realized :param layerNumber: number of the actual layer :param verbose: flag if more output should be given :return: 3-tuple (resultAngle, polar opening, number of runs) """ tol = 1e-2 popt = minimize_scalar( targetFunction, method='bounded', bounds=[minAngle, 75], # bounds of the angle args=[vessel, layerNumber, targetPolarOpening, verbose], options={ "maxiter": 1000, 'disp': 1, "xatol": tol }) if not popt.success: raise Tankoh2Error('Could not finde optimal solution') angle, funVal, iterations = popt.x, popt.fun, popt.nfev if popt.fun > 1 and targetFunction is getPolarOpeningDiffByAngle: # desired polar opening not met. This happens, when polar opening is near fitting. # There is a discontinuity at this point. Switch target function to search from the fitting side. angle, funVal, iterations = optimizeAngle( vessel, targetPolarOpening, layerNumber, verbose, getNegAngleAndPolarOpeningDiffByAngle) else: windLayer(vessel, layerNumber, angle) #angle2 = vessel.estimateCylinderAngle(layerNumber, targetPolarOpening) #r = angle / angle2 return angle, funVal, iterations
def getMaxFibreFailureByAngle(angle, args): """Returns the maximum puck fibre failure index after setting and winding the given angle""" vessel, layerNumber, puckProperties, burstPressure, dropIndicies, verbose = args if hasattr(angle, '__iter__'): angle = angle[0] if angle is not None: actualPolarOpening = windLayer(vessel, layerNumber, angle, verbose) if actualPolarOpening is np.inf: return np.inf maxFF, maxIndex = _getMaxFibreFailure(args) if verbose: log.info(f'angle {angle}, max fibre failure {maxFF}, index {maxIndex}') return maxFF
def getMaxFibreFailureByShift(shift, args): """Returns the maximum puck fibre failure index after setting and winding the given hoop layer shift""" if hasattr(shift, '__iter__'): shift = shift[0] vessel, layerNumber, puckProperties, burstPressure, dropIndicies, verbose = args vessel.setHoopLayerShift(layerNumber, shift, True) actualPolarOpening = windLayer(vessel, layerNumber, verbose=verbose) if actualPolarOpening is np.inf: return np.inf maxFF, maxIndex = _getMaxFibreFailure(args) if verbose: log.info( f'hoop shift {shift}, max fibre failure {maxFF}, index {maxIndex}') return maxFF
def designLayers(vessel, maxLayers, minPolarOpening, puckProperties, burstPressure, runDir, composite, compositeArgs, verbose): """ Strategy: #. Start with hoop layer #. Second layer: #. Maximize layer angle that still attaches to the fitting #. add layer with this angle #. Iteratively perform the following #. Get puck fibre failures #. Check if puck reserve factors are satisfied - if yes end iteration #. Reduce relevant locations to #. 1 element at cylindrical section and #. every element between polar opening radii of 0 and of 70° angle layers #. identify critical element #. if critical element is in cylindrical section #. add hoop layer #. next iteration step #. if most loaded element is in dome area: #. Define Optimization bounds [minAngle, 70°] and puck result bounds #. Minimize puck fibre failue: #. Set angle #. Use analytical linear solver #. return max puck fibre failure #. Apply optimal angle to actual layer #. next iteration step """ vessel.resetWindingSimulation() anglesShifts = [] # list of 2-tuple with angle and shift for each layer show = False save = True layerNumber = 0 iterations = 0 liner = vessel.getLiner() dome = liner.getDome1() radiusDropThreshold = windLayer(vessel, layerNumber, 70) mandrel = vessel.getVesselLayer(layerNumber).getOuterMandrel1() dropRadiusIndex = np.argmin( np.abs(mandrel.getRArray() - radiusDropThreshold)) elementCount = mandrel.getRArray().shape[0] - 1 minAngle, _, _ = optimizeAngle( vessel, minPolarOpening, layerNumber, 1., False, targetFunction=getAngleAndPolarOpeningDiffByAngle) rMax = mandrel.getRArray()[0] dropHoopIndexStart = np.argmax( (-mandrel.getRArray() + rMax) > rMax * 1e-4) - 10 dropHoopIndexEnd = np.argmin( np.abs(mandrel.getRArray() - dome.cylinderRadius * 0.98)) hoopOrHelicalIndex = np.argmin( np.abs(mandrel.getRArray() - dome.cylinderRadius * 0.99)) maxHoopShift = mandrel.getLArray( )[dropHoopIndexEnd] - liner.cylinderLength / 2 dropHoopIndicies = list(range(0, dropHoopIndexStart)) + list( range(dropHoopIndexEnd, elementCount)) dropHelicalIndicies = range(0, hoopOrHelicalIndex) # introduce layer up to the fitting. Optimize required angle printLayer(layerNumber, verbose) angle, _, _ = optimizeAngle( vessel, minPolarOpening, layerNumber, minAngle, False, targetFunction=getNegAngleAndPolarOpeningDiffByAngle) anglesShifts.append((angle, 0)) layerNumber += 1 # add hoop layer resHoop = optimizeHoop(vessel, layerNumber, puckProperties, burstPressure, dropHoopIndicies, maxHoopShift, verbose) shift, funcVal, loopIt, newDesignIndex = resHoop windHoopLayer(vessel, layerNumber, shift) anglesShifts.append((90, shift)) #printLayer(layerNumber, verbose) #windLayer(vessel, layerNumber, 90) #anglesShifts.append((90.,0)) # create other layers for layerNumber in range(layerNumber + 1, maxLayers): printLayer(layerNumber, verbose) elemIdxmax, puckFF = getCriticalElementIdxAndPuckFF( vessel, puckProperties, None, burstPressure) if puckFF.max().max() < 1: if verbose: log.info('End Iteration') # stop criterion reached columns = [ 'lay{}_{:04.1f}'.format(i, angle) for i, (angle, _) in enumerate(anglesShifts) ] puckFF.columns = columns plotDataFrame(False, os.path.join(runDir, f'puck_{layerNumber}.png'), puckFF) layerNumber -= 1 break elif layerNumber > maxLayers: raise Tankoh2Error( 'Reached max layers. You need to specify more initial layers') # add one layer composite = getComposite([a for a, _ in anglesShifts] + [90], [compositeArgs[2]] * (layerNumber + 1), *compositeArgs[1:]) vessel.setComposite(composite) resetVesselAnglesShifts(anglesShifts, vessel) # check zone of highest puck values if elemIdxmax < hoopOrHelicalIndex: resHoop = optimizeHoop(vessel, layerNumber, puckProperties, burstPressure, dropHoopIndicies, maxHoopShift, verbose) resHelical = optimizeHelical(vessel, layerNumber, puckProperties, burstPressure, minPolarOpening, dropHoopIndicies, verbose) if resHoop[1] < resHelical[ 1] * 1.25: # puck result with helical layer must be 1.25 times better # add hoop layer shift, funcVal, loopIt, newDesignIndex = resHoop windHoopLayer(vessel, layerNumber, shift) anglesShifts.append((90, shift)) else: angle, funcVal, loopIt, newDesignIndex = resHelical windLayer(vessel, layerNumber, angle) anglesShifts.append((angle, 0)) else: angle, funcVal, loopIt, newDesignIndex = optimizeHelical( vessel, layerNumber, puckProperties, burstPressure, minPolarOpening, dropHelicalIndicies, verbose) anglesShifts.append((angle, 0)) iterations += loopIt columns = [ 'lay{}_{:04.1f}'.format(i, angle) for i, (angle, _) in enumerate(anglesShifts[:-1]) ] puckFF.columns = columns plotDataFrame(False, os.path.join(runDir, f'puck_{layerNumber}.png'), puckFF, None, vlines=[elemIdxmax, hoopOrHelicalIndex, newDesignIndex], vlineColors=['red', 'black', 'green'], title='puck fibre failure') vessel.finishWinding() results = getLinearResults(vessel, puckProperties, layerNumber, burstPressure) if show or save: plotStressEpsPuck( show, os.path.join(runDir, f'sig_eps_puck_{layerNumber}.png') if save else '', *results) thicknesses = getLayerThicknesses(vessel) columns = [ 'lay{}_{:04.1f}'.format(i, angle) for i, (angle, _) in enumerate(anglesShifts) ] thicknesses.columns = columns plotDataFrame(show, os.path.join(runDir, f'thicknesses_{getTimeString()}.png'), thicknesses, title='layer thicknesses') # get volume and surface area stats = vessel.calculateVesselStatistics() frpMass = stats.overallFRPMass # in [kg] volume = liner.getVolume() # [l] r, x = dome.getRCoords(), dome.getXCoords() areaDome = np.pi * (r[:-1] + r[1:]) * np.sqrt((r[:-1] - r[1:])**2 + (x[:-1] - x[1:])**2) area = 2 * np.pi * liner.cylinderRadius * liner.cylinderLength + 2 * np.sum( areaDome) # [mm**2] area *= 1e-6 # [m**2] return frpMass, volume, area, composite, iterations, anglesShifts
def resetVesselAnglesShifts(anglesShifts, vessel): for layerNumber, (angle, shift) in enumerate(anglesShifts): if abs(angle - 90) < 1e-2: windHoopLayer(vessel, layerNumber, shift) else: windLayer(vessel, layerNumber, angle)