x=string.split('-') return (int(x[0]),int(x[1])) else: return int(string) parser = argparse.ArgumentParser( description="Plots for Pressure Swing Adsorption Oxygen Concentration simulation", epilog=''' ''') parser.add_argument("files", nargs='+',type=str, help="pickle files to plot", default=None) parser.add_argument('-c', '--cycles', type=parseIntRange, help="cycle or range of cycles to plot, .e.g. 4 or 0-2", default=None) parser.add_argument('-n', '--nopause', action='store_true', help="do not pause after generating plots") options = parser.parse_args() print('git revision:{}'.format(util.get_git_commit())) class AttrDict(dict): def __init__(self, *args, **kwargs): super(AttrDict, self).__init__(*args, **kwargs) self.__dict__ = self for file in options.files: with open(file, 'rb') as fp: data=pickle.load(fp) print('loaded {}'.format(file)) print('cyles {}'.format(options.cycles)) head,tail=os.path.split(file) print('head={} tail={}'.format(head,tail)) #take the part up to the dash in tail, and add something
def simulate(override_param, outdir=None,params_file='params',verbose=False): #params to override the defaults from params.py #plots: create output plots #pause: if creating plots, pause when they are displayed on screen for <Enter> #returns metrics of simulation as a dict, and the full params used #writes a log file (and optional plots) into outdir. use outdir=None for #no log file and instead direct all outout to stdout. plots will go into #current directory in that case. #params is the file to import (default is params) #roi is (t1,t2) time range for the spatial plots #returns out_place: directory+logcode prefix that it used for log file global gverbose gverbose=verbose params=importlib.import_module(params_file) np.seterr(all='warn') global param global norm global dg global count global tstart #we will alter print statement for this module to go into the log file global print if outdir is None: dbg=mylog.Log(outdir=None,descr='psa') OUTDIR='./' else: OUTDIR=outdir # for log files and png files dbg=mylog.Log(outdir=OUTDIR,descr='psa') #change the print function in the module to print to the log file old_print=print print=dbg.print param=params.create_param(override_param, dbg.print) norm=create_norm() print('simulation params: time 0 to {}, step {}'.format(param.end_time,param.tstep)) print('step time might be increased if long cycle time') print('normalization velocity {:8.3f} m/s'.format(param.norm_v0)) print('normalization of time norm.t0 {:8.3f} sec'.format(norm.t0)) print('starting with params import file: {}'.format(params)) state1=init(param,norm) state2=init(param,norm) prod=AttrDict() prod.Pprod=1e5/norm.P0 # 1 atm to start prod.yprod=param.feed_O2 # fract of O2 in product tank #create dimensionless groups for the constants needed in the simulation dg=create_dgroups(norm,param,state1) np.set_printoptions(precision=5, edgeitems=6, linewidth=95, suppress=False) pp=pprint.PrettyPrinter(indent=2) print('git revision:{}'.format(util.get_git_commit())) print('log code: {}'.format(dbg.log_code)) sys.stdout.write('log file prefix code: {}\n'.format(dbg.log_code)) print('version:{}'.format(vinfo)) print('plt {}'.format(matplotlib.__version__)) #print out the parameters and numbers for the record print('dimensionless groups:\n',pp.pformat(dg.__dict__)) print('parameters:\n',pp.pformat(param.__dict__)) #create directory and log_code to prepend to out png files util.safe_mkdir(OUTDIR) out_place=os.path.join(OUTDIR,dbg.log_code) #write to screen, not log file sys.stdout.write('out_place={}\n'.format(out_place)) #output is to 1 ATM pressure (abs) x01=np.hstack((state1.P,state1.T,state1.Tw,state1.xA,state1.xB,state1.yA,state1.yB)) x02=np.hstack((state2.P,state2.T,state2.Tw,state2.xA,state2.xB,state2.yA,state2.yB)) x03=np.array([prod.Pprod, prod.yprod]) print(x01.shape,x02.shape,x03.shape) x0=np.hstack((x01,x02,x03)) tstart=time.time() #time normalized by norm.t0 #Vectorized means that myfun can take a matrix of values to evaluate dx/dt at, #not that x0 is a matrix compute_alpha(verbose=True) # just print out the values for the log file #we add a small number because we want to get full cycle at end #also, increase tstep is a long cycle time: param.tstep=max(param.tstep, param.cycle_time/100) end_simulation=param.end_time+1e-6 t_span=(0,end_simulation) #we want to include the end_simulation time ev=np.arange(0,end_simulation,param.tstep) print('t_span {}'.format(t_span)) #print('t_eval {}'.format(ev)) sys.stdout.write('Calling ODE solver\n') bunch=scipy.integrate.solve_ivp(myfun, t_span, x0, vectorized=False, t_eval=ev, method='BDF') ### method='Radau') if not bunch.success: print(bunch.message) sys.stdout.write(bunch.message) return None print('evaluated at {} places'.format(bunch.t.size)) print('num eval of RHS {}'.format(bunch.nfev)) print('num eval of Jacobian {}'.format(bunch.njev)) print('total number of elements in data is {}'.format(bunch.y.size)) print('number of elements > 2 = {}'.format(np.sum(bunch.y>2))) print('number of elements > 1 = {}'.format(np.sum(bunch.y>1))) print('number of elements < 0 = {}'.format(np.sum(bunch.y<0))) #t will be array of time points (t_points) #y will be array of (n x t_points) values N=param.N #take the output data from ODE solver and place in data.<name> for plotting data=AttrDict() data.data1,data.data2,data.datap=data_to_state(bunch.y) data.t=bunch.t print('bunch.t',data.t) data.param=param print_state_ranges(data.data1) print_state_ranges(data.data2) #compute xAs at each point data.data1.xAs,data.data1.xBs=adsorp_equilibrium(data.data1.yA,data.data1.yB,data.data1.P) data.data2.xAs,data.data2.xBs=adsorp_equilibrium(data.data2.yA,data.data2.yB,data.data2.P) #compute velocity at each point data.data1.V=compute_vel(data.data1) data.data2.V=compute_vel(data.data2) #Output key statistics in log file with keywords #get indexes for full cycle print('CYCLE {:8.2f}'.format(param.cycle_time)) print('VENT {:8.2f}'.format(param.vent_time)) #indexes to select over - the last 2 cycles for container 1 r=np.argmax(bunch.t>(max(bunch.t)-2*param.cycle_time)) ret=AttrDict() ret.time=datetime.datetime.now().replace(microsecond=0).isoformat() ret.end_time=end_simulation ret.cycle_time=param.cycle_time ret.vent_time=param.vent_time ret.norm_t0=norm.t0 ret.real_cycle_time=param.cycle_time*norm.t0 ret.real_vent_time=param.vent_time*norm.t0 #these are all min,max,mean for the last cycle of container 1 #for container 1, we calculate parameters for all the odd cycles and store in # a list. To get last one, use index [-1] idx=0 ret.container_y=[] ret.prod_pressure=[] ret.prod_y=[] ret.prod_flow_rate=[] while True: start=param.cycle_time*idx stop=param.cycle_time*(idx+1) if stop > max(bunch.t): break #create a mask of the times for this odd cycle (starting with 1st one) mask=np.logical_and(start <= bunch.t, bunch.t <= stop) ret.container_y.append([np.min(data.data1.yA[-1,mask]), np.max(data.data1.yA[-1,mask]), np.mean(data.data1.yA[-1,mask])]) ret.prod_pressure.append([np.min(data.datap.Pprod[0,mask]), np.max(data.datap.Pprod[0,mask]), np.mean(data.datap.Pprod[0,mask])]) ret.prod_y.append([np.min(data.datap.yprod[0,mask]), np.max(data.datap.yprod[0,mask]), np.mean(data.datap.yprod[0,mask])]) #compute product flow in LPM ret.prod_flow_rate.append([product_flow_rate(np.min(data.datap.Pprod[0,mask])), product_flow_rate(np.max(data.datap.Pprod[0,mask])), product_flow_rate(np.mean(data.datap.Pprod[0,mask]))]) idx+=2 ret.logcode=dbg.log_code print('results:\n',pp.pformat(ret)) print('These are repr() outputs for programmatic reading') print('PARAM {}'.format(repr(param))) print('RESULT {}'.format(repr(ret))) data.ret=ret #save to pickle file pickle_name='{}-data.pickle'.format(out_place) with open(pickle_name, 'wb') as fp: pickle.dump(data,fp) #close the log file because might be called again before exiting and will #want a new log file for that simulation dbg.close() #restore print function print=old_print return data, ret, param, pickle_name, out_place