### Locate Solstice Installation and Programs Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/find_solstice.html Contains the core logic for finding the Solstice root directory via the Windows Registry and resolving executable paths. ```python # This script finds the location of Solstice from the Windows Registry, assuming # it was installed with ANU's NSIS-based installer. It works with Python 2.7 and # Python 3.8, with both 32 and 64-bit versions, on both Windows 7 and Windows 10 # -- John Pye, April 2020 import sys, platform, os [docs]def find_solstice_root(version_required=None,verbose=0): """Locate the place where Solstice files are installed on Windows ``Arguments`` * version_required (None or str): if not None, then enforce this specified version of Solstice (eg '0.9.0') * verbose (bool): if True, output file locations to stderr ``Return`` * dirn (str): the directory where Solstice is installed in the Windows system """ if verbose: sys.stderr.write("Python is running from %s\n"%(sys.executable,)) if not platform.system()=="Windows": raise RuntimeError("This function is only for Windows") # otherwise... if sys.version_info[0] < 3: if verbose: sys.stderr.write("Python 2, ") import _winreg as winreg else: if verbose: sys.stderr.write("Python 3, ") import winreg if sys.maxsize > 2**32: if verbose: sys.stderr.write("64-bit\n") # 64-bit Python key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,r"Software\Meso-Star\Solstice",0,winreg.KEY_READ) else: if verbose: sys.stderr.write("32-bit\n") key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,r"Software\Meso-Star\Solstice",0,winreg.KEY_WOW64_64KEY+winreg.KEY_READ) dirn, typd = winreg.QueryValueEx(key, 'root') ver, typv = winreg.QueryValueEx(key, 'version') if version_required: assert ver == version_required return dirn [docs]def find_prog(name,version_required=None): """Find the path to any required Solstice executable program ``Arguments`` * name (str): stem-name (eg 'solpp') of the program (eg 'solpp.exe') required * version_required (None or str): if not None, then enforce this specified version of Solstice (e.g. '0.9.0') ``Return`` * path (str): path of the required Solstice executable program """ if platform.system()=="Windows": path = os.path.join(find_solstice_root(version_required),"bin","%s.exe"%(name,)); if not os.path.exists(path): raise RuntimeError("Program '%s' was not found at path '%s'"%(name,path)) return path else: # assume all solstice programs are on the PATH import subprocess with open(os.devnull, 'wb') as DEVNULL: proc = subprocess.run(['which',name],stdout=DEVNULL) if proc.returncode: raise RuntimeError("Program '%s' was not found in the PATH" %(name)) return name if __name__=="__main__": dirn = find_solstice_root('0.9.0',verbose=1) sys.stderr.write("Solstice is installed in %s\n\n" %(dirn,)) # works both python2+3 :o) import subprocess, os spath = os.path.join(dirn,"bin","solstice.exe") # check that installed version of solstice is 0.9.0: proc = subprocess.run([spath,"--version"],encoding='utf-8',stdout=subprocess.PIPE) assert proc.stdout.strip() == "Solstice 0.9.0" # output the help text from solstice: subprocess.run([spath,"-h"]) ``` -------------------------------- ### Calculate Heliostat Field Performance Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/cal_field.html Example workflow for calculating field performance and exporting results to a VTK file for visualization in ParaView. ```python >>> from solsticepy.cal_field import * >>> pos_and_aiming=np.loadtxt('./pos_and_aiming.csv', delimiter=',', skiprows=2) #load the field layout file (refer to cal_layout.py) >>> pos=pos_and_aiming[:,:3] >>> aim=pos_and_aiming[:,4:] >>> azimuth=np.r_[0.] >>> zenith=np.r_[12.] >>> field=FieldPF(np.r_[0,1,0]) >>> sun_vec=field.get_solar_vector(azimuth, zenith) >>> norms=field.get_normals(towerheight=70., hstpos=pos, sun_vec=sun_vec) >>> COORD, TRI, ele, nc=field.mesh_heliostat_field(width=10., height=8., normals=norms, hstpos=pos) >>> cos=field.get_cosine(hst_norms=norms, sun_vec=sun_vec) >>> savedir='./field.vtk' >>> COS=np.repeat(cos, ele) >>> DATA={'cos':COS} >>> NORMS=np.repeat(norms, ele, axis=0) >>> gen_vtk(savedir, COORD.T, TRI, NORMS, True, DATA) ``` -------------------------------- ### find_solstice_root(version_required=None, verbose=0) Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/find_solstice.html Locates the directory where Solstice is installed on a Windows system by querying the Windows Registry. ```APIDOC ## find_solstice_root(version_required=None, verbose=0) ### Description Locates the installation directory of Solstice on Windows. This function requires the Windows platform. ### Parameters - **version_required** (str, optional) - If provided, enforces a specific version of Solstice (e.g., '0.9.0'). - **verbose** (bool, optional) - If True, outputs file locations to stderr. ### Returns - **dirn** (str) - The directory path where Solstice is installed. ``` -------------------------------- ### Initialize Sun Parameters Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/gen_yaml.html Demonstrates creating a Sun object and generating its YAML representation. ```python >>> sun = Sun(dni=1000, sunshape='buie', csr=0.2) >>> print(sun.yaml(spectrum = "*solar_spectrum")) ``` -------------------------------- ### Generate a radial-stagger heliostat field Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/cal_layout.html Example usage of the radial_stagger function to calculate heliostat positions and aiming points. ```python >>> from solsticepy.cal_layout import * >>> latitude=34. >>> hst_width=13. >>> hst_height=10. >>> hst_z=5. >>> target_area=75715. >>> num_hst=target_area/hst_width/hst_height*2 # create a twice large field, so that the inefficient heliostat can be trimmed-off after optical simulations >>> tower_height=110. >>> R1=40. # the distance from the first row to the bottom of the tower >>> fb=0.6 >>> casefolder='.' # replace it as the directory of your case folder >>> plot=False # if you want to plot the field layout, set it as True >>> pos_and_aim=radial_stagger(latitude, num_hst, hst_width, hst_height, hst_z, tower_height, R1, fb, savedir=casefolder,plot=plot) ``` -------------------------------- ### Master.__init__ Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/master.html Initializes the Master simulation controller, establishing the case directory and configuring processor usage. ```APIDOC ## Master.__init__(casedir='.', nproc=None) ### Description Sets up the Solstice simulation environment by establishing the case folder and configuring parallel processing settings. ### Parameters - **casedir** (str) - Optional - The case directory path (default: '.') - **nproc** (int) - Optional - Number of processors for parallel execution; None uses all available processors. ``` -------------------------------- ### Master Class Initialization Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/master.html Initializes the simulation environment by setting the case directory and processor count. ```python def __init__(self, casedir='.', nproc=None): """Set up the Solstice simulation, i.e. establishing the case folder, calling the Solstice program and post-processing the results ``Argument`` * casedir (str): the case directory * nproc (int): number of processors, e.g. nproc=1 will run in serial mode, nproc=4 will run with 4 processors in parallel nproc=None will run with any number of processors that are available """ self.casedir=os.path.abspath(casedir) self.nproc=nproc if not os.path.exists(self.casedir): os.makedirs(self.casedir) assert os.path.isdir(casedir) sys.stderr.write("Case directory is '%s'\n" % (yellow(self.casedir),)) ``` -------------------------------- ### Master.run Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/master.html Executes an optical ray-tracing simulation for a specific sun position. ```APIDOC ## Master.run(azimuth, elevation, num_rays, rho_mirror, dni, folder, gen_vtk=False, printresult=False, verbose=False, system='crs') ### Description Runs an optical simulation for a single sun position using Solstice. Optionally generates VTK files for rendering in Paraview. ### Parameters - **azimuth** (float) - Required - Azimuth angle (East towards North). - **elevation** (float) - Required - Elevation angle. - **num_rays** (int) - Required - Number of rays to cast. - **rho_mirror** (float) - Required - Mirror reflectivity. - **dni** (float) - Required - Direct normal irradiance (W/m2). - **folder** (str) - Required - Output folder for simulation results. - **gen_vtk** (boolean) - Optional - If True, generates .vtk files. - **system** (str) - Optional - System type: 'crs' or 'dish'. ``` -------------------------------- ### Execute process_raw_results from Command Line Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/process_raw.html Entry point for processing raw results using command-line arguments and printing the total efficiency to stderr. ```python if __name__=='__main__': eta,pf_hst = proces_raw_results(sys.argv[1], sys.argv[2], sys.argv[3]) sys.stderr.write('\nTotal efficiency: %s\n'%(repr(eta),)) ``` -------------------------------- ### Running Optical Simulations Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/master.html Executes the Solstice ray-tracing simulation and performs optional post-processing tasks like VTK generation. ```python def run(self, azimuth, elevation, num_rays, rho_mirror, dni, folder, gen_vtk=False, printresult=False, verbose=False, system='crs'): """Run an optical simulation (one sun position) using Solstice * `azimuth` (float): the azimuth angle of the ray-tracing simulation in Solstice, counted from East towards to North * `elevation` (float): the elevation angle of the ray-tracing simulation in Solstice * `num_rays` (int): number of rays to be cast in the ray-tracing simulation * `rho_mirror`(float): reflectivity of mirrors, required for results post-processing * `dni` (float): the direct normal irradiance (W/m2), required to obtain performance of individual heliostat * `gen_vtk` (boolean): if True, generate .vtk files for rendering in Paraview * `system` (str): 'crs' for a central receiver system, or 'dish' for a parabolic dish system Returns: no return value (results files are created and written) """ YAML_IN = self.in_case(self.casedir, 'input.yaml') RECV_IN = self.in_case(self.casedir, 'input-rcv.yaml') # main raytrace if self.nproc==None: run_prog("solstice",['-D%s,%s'%(azimuth,elevation),'-v','-n',num_rays,'-R',RECV_IN,'-fo',self.in_case(folder, 'simul'),YAML_IN]) else: run_prog("solstice",['-D%s,%s'%(azimuth,elevation),'-v', '-t', self.nproc, '-n',num_rays,'-R',RECV_IN,'-fo',self.in_case(folder, 'simul'),YAML_IN]) folder=os.path.abspath(folder) if gen_vtk and verbose: dirn = os.getcwd() try: os.chdir(folder) # Read "simul" results and produce a text file with the raw results run_prog('solppraw',[self.in_case(folder, 'simul')]) # post processing run_prog("solstice",['-D%s,%s'%(azimuth,elevation),'-g','format=obj:split=geometry','-fo',self.in_case(folder, 'geom'),YAML_IN]) # run a short raytrace to produce some ray paths run_prog("solstice",['-D%s,%s'%(azimuth,elevation),'-q','-n','100','-R',RECV_IN,'-p','default',YAML_IN], output_file=self.in_case(folder, 'solpaths')) # Read "simul" results and produce receiver files (.vtk) of incoming and/or absorbed solar flux per-primitive run_prog('solmaps',[self.in_case(folder, 'simul')]) # Read "geom" and "simul" file results and produce primaries and receivers files (.vtk), and .obj geometry files run_prog('solpp',[self.in_case(folder, 'geom'),self.in_case(folder, 'simul')]) # Read "solpaths" file and produce readable file (.vtk) by paraview to visualize the ray paths run_prog('solpaths',[self.in_case(folder, 'solpaths')]) finally: os.chdir(dirn) ``` -------------------------------- ### solsticepy.Master Source: https://solsticepy.readthedocs.io/en/latest/index.html Class for setting up and running Solstice simulations. ```APIDOC ## solsticepy.Master(casedir='.', nproc=None) ### Description Set up the Solstice simulation, establishing the case folder, calling the Solstice program, and post-processing results. ### Parameters - **casedir** (str) - Optional - The case directory. - **nproc** (int) - Optional - Number of processors for parallel execution. ``` -------------------------------- ### Master.run_annual Source: https://solsticepy.readthedocs.io/en/latest/index.html Runs a list of optical simulations to obtain annual performance. ```APIDOC ## Master.run_annual(nd, nh, latitude, num_rays, num_hst, rho_mirror, dni, gen_vtk=False, verbose=False) ### Description Run a list of optical simulations to obtain annual performance (lookup table) using Solstice. ### Parameters - **nd** (int) - Required - Number of rows in lookup table. - **nh** (int) - Required - Number of columns in lookup table. - **latitude** (float) - Required - Latitude angle (deg). - **num_rays** (int) - Required - Number of rays to be cast. - **num_hst** (int) - Required - Number of heliostats. - **rho_mirror** (float) - Required - Reflectivity of mirrors. - **dni** (float) - Required - Direct normal irradiance (W/m2). ### Returns - **table** (numpy array) - Annual optical efficiency lookup table. - **ANNUAL** (numpy array) - Annual output of each heliostat. ``` -------------------------------- ### Run Annual Performance Simulation Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/master.html Executes a series of optical simulations to generate an annual performance lookup table. ```python def run_annual(self, nd, nh, latitude, num_rays, num_hst,rho_mirror,dni, gen_vtk=False,verbose=False): """Run a list of optical simulations to obtain annual performance (lookup table) using Solstice ``Arguments`` * nd (int): number of rows in the lookup table (discretisation of the declination angle) * nh (int): number of columns in the lookup table (discretisation of the solar hour angle) * latitude (float): the latitude angle of the plan location (deg) * num_rays (int): number of rays to be cast in the ray-tracing simulation * num_hst (int): number of heliostats * nproc (int): number of processors, e.g. nproc=1 will run in serial mode, nproc=4 will run with 4 processors in parallel nproc=None will run with any number of processors that are available * rho_mirror (float): reflectivity of mirrors, required for results post-processing * dni (float): the direct normal irradiance (W/m2), required to obtain performance of individual heliostat * gen_vtk (bool): True - perform postprocessing for visualisation of each individual ray-tracing scene (each sun position), False - no postprocessing for visualisation ``Return`` * table (numpy array), the annual optical efficiency lookup table * ANNUAL (numpy array), the annual output of each heliostat """ YAML_IN = self.in_case(self.casedir, 'input.yaml') RECV_IN = self.in_case(self.casedir, 'input-rcv.yaml') sun=SunPosition() AZI, ZENITH,table,case_list=sun.annual_angles(latitude, casefolder=self.casedir, nd=nd, nh=nh) case_list=case_list[1:] SOLSTICE_AZI, SOLSTICE_ELE=sun.convert_convention('solstice', AZI, ZENITH) # performance of individual heliostat is recorded # TODO note, DNI is not varied in the simulation, # i.e. performance is not dni-weighted ANNUAL=np.zeros((num_hst, 9)) run=np.r_[0] for i in range(len(case_list)): c=int(case_list[i,0].astype(float)) if c not in run: azimuth=SOLSTICE_AZI[c-1] elevation=SOLSTICE_ELE[c-1] #if np.sin(elevation*np.pi/180.)>=1.e-5: # dni=1618.*np.exp(-0.606/(np.sin(elevation*np.pi/180.)**0.491)) #else: # dni=0. sys.stderr.write("\n"+green('Sun position: %s \n'%c)) print('azimuth: %.2f'% azimuth, ', elevation: %.2f'%elevation) onesunfolder=os.path.join(self.casedir,'sunpos_%s'%(c)) # run solstice if elevation<1.: # 1 degree efficiency_total=ufloat(0,0) performance_hst=np.zeros((num_hst, 9)) else: efficiency_total, performance_hst=self.run(azimuth, elevation, num_rays, rho_mirror, dni, folder=onesunfolder, gen_vtk=gen_vtk, printresult=False, verbose=verbose) sys.stderr.write(yellow("Total efficiency: {:f}\n".format(efficiency_total))) ANNUAL+=performance_hst else: ANNUAL+=performance_hst for a in range(len(table[3:])): for b in range(len(table[0,3:])): val=re.findall(r'\d+', table[a+3,b+3]) if val==[]: table[a+3,b+3]=0 else: if c==float(val[0]): table[a+3,b+3]=efficiency_total.nominal_value run=np.append(run,c) annual_title=np.array(['Q_solar','Q_cosine', 'Q_shade', 'Q_hst_abs', 'Q_block', 'Q_atm', 'Q_spil', 'Q_refl', 'Q_rcv_abs']) ANNUAL=np.vstack((annual_title, ANNUAL)) if verbose: np.savetxt(self.casedir+'/lookup_table.csv', table, fmt='%s', delimiter=',') np.savetxt(self.casedir+'/result-heliostats-annual-performance.csv', ANNUAL, fmt='%s', delimiter=',') sys.stderr.write("\n"+green("Lookup table saved.\n")) sys.stderr.write(green("Completed successfully.\n"+"\n")) return table, ANNUAL ``` -------------------------------- ### Organize Raw Flux Data Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/process_raw.html Constructs a NumPy array containing raw simulation results for sun parameters, flux values, and virtual plane data. ```python vir_area=get_rc(num_res+2+num_rec-1,2) vir_income=get_rc(num_res+2+num_rec-1,3) vir_income_err=get_rc(num_res+2+num_rec-1,4) raw_res=np.array([ ['name','value', 'error'] ,['sun_azimuth', azimuth,''] ,['sun_elevation', elevation, ''] ,['num hst', num_hst,''] ,['num rays',num_rays, ''] ,['potential flux', potential, potential_err] ,['absorbed flux', absorbed, absorbed_err] ,['Cosine factor', Fcos, Fcos_err] ,['shadow loss', shadow_loss, shadow_err] ,['Mising loss', missing_loss, missing_err] ,['materials loss', material_loss, material_loss_err] ,['atomospheric loss', atmospheric_loss, atmospheric_err] ,['','',''] ,['','',''] ,['Virtual plane','',''] ,['area', vir_area, ''] ,['income flux', vir_income,vir_income_err]]) for i in range(num_apertures): aperture_i = np.array([ ['','',''] ,['Aperture', i,''] ,['area', rec_area[i], ''] ,['front income flux', rec_front_income[i], rec_front_income_err[i]] ,['back income flux', rec_back_income[i], rec_back_income_err[i]] ,['front absorbed flux', rec_front_absorbed[i], rec_front_absorbed_err[i]] ,['back absorbed flux', rec_back_absorbed[i], rec_back_absorbed_err[i]] ,['front efficiency', rec_front_eff[i], rec_front_eff_err[i]] ,['back efficiency', rec_back_eff[i], rec_back_eff_err[i]]]) raw_res=np.vstack((raw_res, aperture_i)) ``` -------------------------------- ### process_raw_results_dish(rawfile, savedir, rho_mirror, dni, verbose=False) Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/process_raw.html Processes the raw Solstice 'simul' output file into readable CSV files for dish systems. It parses the simulation data, calculates performance metrics, and writes the organized results to the specified directory. ```APIDOC ## process_raw_results_dish ### Description Processes the raw Solstice 'simul' output file into readable CSV files for dish systems. It parses the simulation data, calculates performance metrics, and writes the organized results to the specified directory. ### Parameters - **rawfile** (str) - Required - The directory of the 'simul' file generated by Solstice. - **savedir** (str) - Required - The directory for saving the organised results. - **rho_mirror** (float) - Required - Mirror reflectivity (needed for reporting energy sums). - **dni** (float) - Required - The direct normal irradiance (W/m2), required to obtain performance of individual heliostat. - **verbose** (bool) - Optional - Enables verbose output if set to True. ### Returns - **efficiency_total** (float) - The total optical efficiency. ``` -------------------------------- ### File Path Management Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/master.html Joins a filename with the case directory and ensures the target folder exists. ```python def in_case(self, folder, fn): """Joining a file name with the case directory ``Argument`` * fn (str): file name ``Return`` * a joining directory of the file in the case directory """ if not os.path.exists(folder): os.makedirs(folder) assert os.path.isdir(folder) return os.path.join(folder,fn) ``` -------------------------------- ### Process Simulation Results Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/master.html Handles result processing for different system configurations like dish or multi-aperture systems. ```python if system=='dish': eta=process_raw_results_dish(self.in_case(folder, 'simul'), folder, rho_mirror, dni, verbose=verbose) if printresult: sys.stderr.write('\n' + yellow("Total efficiency: {:f}\n".format(eta))) sys.stderr.write(green("Completed successfully.\n")) return eta else: if system=='multi-aperture': eta, performance_hst=process_raw_results_multi_aperture(self.in_case(folder, 'simul'), folder,rho_mirror, dni, verbose=verbose) else: eta, performance_hst=process_raw_results(self.in_case(folder, 'simul'), folder,rho_mirror, dni, verbose=verbose) if printresult: sys.stderr.write('\n' + yellow("Total efficiency: {:f}\n".format(eta))) sys.stderr.write(green("Completed successfully.\n")) return eta, performance_hst ``` -------------------------------- ### Calculate and Visualize Heliostat Field Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/cal_field.html Demonstrates loading heliostat positions, calculating solar vectors and normals, and exporting the field geometry to a VTK file. ```python if __name__=='__main__': #pos_and_aiming=np.loadtxt('/media/yewang/Work/svn_ye/Solstice-tutorial/cases/2-Validation/layout.csv', delimiter=',', skiprows=2) pos_and_aiming=np.loadtxt('./pos_and_aiming.csv', delimiter=',', skiprows=2) pos=pos_and_aiming[:,:3] aim=pos_and_aiming[:,4:] azimuth=np.r_[0.] zenith=np.r_[12.] field=FieldPF(np.r_[0,1,0]) sun_vec=field.get_solar_vector(azimuth, zenith) norms=field.get_normals(towerheight=70., hstpos=pos, sun_vec=sun_vec) #field.heliostat(10, 8) COORD, TRI, ele, nc=field.view_heliostats(width=10., height=8., normals=norms, hstpos=pos) cos=field.get_cosine(hst_norms=norms, sun_vec=sun_vec) savedir='./field.vtk' COS=np.repeat(cos, ele) DATA={'cos':COS} NORMS=np.repeat(norms, ele, axis=0) gen_vtk(savedir, COORD.T, TRI, NORMS, True, DATA) ``` -------------------------------- ### Calculating Energy Balance Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/process_raw.html Computes energy components and organizes them into a summary array using ufloat for uncertainty handling. ```python Qtotal=ufloat(potential, 0) Fcos=ufloat(Fcos,Fcos_err) Qcos=Qtotal*(1.-Fcos) Qshade=ufloat(shadow_loss,shadow_err) Qfield_abs=(Qtotal-Qcos-Qshade)*(1.-float(rho_mirror)) Qattn=ufloat(atmospheric_loss, atmospheric_err) Qabs=ufloat(absorbed, absorbed_err) Qspil=ufloat(vir_income,vir_income_err)-Qabs Qrefl=ufloat(rec_front_income,rec_front_income_err)+ufloat(rec_back_income,rec_back_income_err)-Qabs Qblock=Qtotal-Qcos-Qshade-Qfield_abs-Qspil-Qabs-Qrefl-Qattn organised=np.array([ ['Name', 'Value', '+/-Error'] ,['Qall (kW)', Qtotal.n/1000., Qtotal.s/1000.] ,['Qcos (kW)', Qcos.n/1000.,Qcos.s/1000.] ,['Qshad (kW)', Qshade.n/1000., Qshade.s/1000.] ,['Qfield_abs (kW)', Qfield_abs.n/1000., Qfield_abs.s/1000.] ,['Qblcok (kW)', Qblock.n/1000., Qblock.s/1000.] ,['Qattn (kW)',Qattn.n/1000., Qattn.s/1000.] ,['Qspil (kW)', Qspil.n/1000., Qspil.s/1000.] ,['Qrefl (kW)', Qrefl.n/1000.,Qrefl.s/1000.] ,['Qabs (kW)', Qabs.n/1000., Qabs.s/1000.] ,['rays', num_rays,'-'] ]) efficiency_total=Qabs/Qtotal ``` -------------------------------- ### Master.in_case Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/master.html Utility method to join a filename with the established case directory. ```APIDOC ## Master.in_case(folder, fn) ### Description Joins a file name with the specified folder path within the case directory. ### Parameters - **folder** (str) - Required - The target folder path. - **fn** (str) - Required - The file name to join. ### Returns - (str) - The full path joining the folder and the file name. ``` -------------------------------- ### Generate Spectral Data YAML Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/gen_yaml.html Constructs YAML entries for solar spectrum and mirror reflectivity data based on spectral configuration. ```python if spectral: I_sun=SolarSpectrum() # CREATE the spectrum for the sun iyaml+='- spectrum: &solar_spectrum \n' for i in range(0,len(I_sun)-1): iyaml+=' - {wavelength: %s, data: %s }\n' % (I_sun[i][0],I_sun[i][1]) i = len(I_sun)-1 iyaml+=' - {wavelength: %s, data: %s }\n' % (I_sun[i][0],I_sun[i][1]) iyaml+='\n' # CREATE the spectrum for the reflectivity (mirror) mirror_rho= MirrorRhoSpectrum() mirror_ref=mirror_rho for i in range(0,len(mirror_rho)): mirror_ref[i][0] = mirror_rho[len(mirror_rho)-1-i][0]/1000. mirror_ref[i][1] = mirror_rho[len(mirror_rho)-1-i][1]/100. mirror_ref.append([4,0.9]) iyaml+='- spectrum: &%s \n' % 'ref_mirror' for i in range(0,len(mirror_ref)-1): iyaml+=' - {wavelength: %15.8e, data: %15.8e }\n' % (float(mirror_ref[i][0]),float(mirror_ref[i][1])) i = len(mirror_ref)-1 iyaml+=' - {wavelength: %15.8e, data: %15.8e }\n' % (float(mirror_ref[i][0]),float(mirror_ref[i][1])) iyaml+='\n' ``` -------------------------------- ### Exporting formatted results to CSV Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/process_raw.html This snippet demonstrates how to structure simulation results into a NumPy array and save them as a CSV file. ```python res=np.array([ ['Name', 'Value (kW)', 'eta Ratio'] ,['Qall', Qtot, 1] ,['Qcos', Qcos, eta_cos] ,['Qshad', Qshad, eta_shad] ,['Qfield_abs', Qhst, eta_hst] ,['Qblcok', Qblock, eta_block] ,['Qattn',Qattn, eta_attn] ,['Qspil ', Qspil,eta_spil] ,['Qrefl', Qrefl, eta_refl] ,['Qabs ', Qabs, eta_abs] ,['After trimming', 'postprocessed results','-'] ]) np.savetxt(casedir+'/des_point/result-formatted-designed.csv', res, fmt='%s', delimiter=',') ``` -------------------------------- ### find_prog(name, version_required=None) Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/find_solstice.html Finds the full path to a specific Solstice executable program. ```APIDOC ## find_prog(name, version_required=None) ### Description Returns the path to a required Solstice executable. On Windows, it resolves the path based on the installation root; on other platforms, it checks the system PATH. ### Parameters - **name** (str, required) - The stem-name of the program (e.g., 'solpp' for 'solpp.exe'). - **version_required** (str, optional) - If provided, enforces a specific version of Solstice. ### Returns - **path** (str) - The absolute path to the executable. ``` -------------------------------- ### Process raw simulation results Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/process_raw.html Parses a raw Solstice simulation file and organizes the data into CSV files. Requires the raw file path, save directory, mirror reflectivity, and DNI value. ```python import numpy as np import re import sys import os from uncertainties import ufloat from uncertainties.umath import * from .output_motab import output_motab [docs]def process_raw_results(rawfile, savedir,rho_mirror,dni,verbose=False): """Process the raw Solstice `simul` output into readable CSV files for central receiver systems ``Arguments`` * rawfile (str): the directory of the `simul` file that generated by Solstice * savedir (str): the directory for saving the organised results * rho_mirror (float): mirror reflectivity (needed for reporting energy sums) * dni (float): the direct normal irradiance (W/m2), required to obtain performance of individual heliostat * verbose (bool), write results to disk or not ``Returns`` * efficiency_total (float): the total optical efficiency * performance_hst (numpy array): the breakdown of losses of each individual heliostat * The simulation results are created and written in the `savedir` """ # FIXME this approach seems fundamentally a bit messy... we are carefully # load the text output from 'solppraw' and then assuming that we can # correctly find the right bits of data in the right place. Wouldn't it # be easier and more robust to modify solppraw to create output that can # be directly loaded, along with data labels, eg a YAML file? Or to # create 'result-raw.csv' directly? rows = [] index = 0 with open(rawfile) as f: for r in f.readlines(): if index<20: pass #sys.stderr.write("Line %d: %s"%(index,r)) if r[0] == "#": #sys.stderr.write("^------Comment\n") #comment line rows.append([r]) else: rows.append(r.split()) index+=1 # sun direction #sys.stderr.write("SUN DIRECTION?\n") #sys.stderr.write(rows[0][0]+"\n") sun=re.findall("[-+]?\d*\.\d+|\d+", rows[0][0]) #sys.stderr.write("SUN = %s\n"%(repr(sun),)) azimuth=sun[0] elevation=sun[1] def get_rc(row,col): return float(rows[row][col]) # Global results num_res=int(get_rc(1,0)) # number of global results num_rec=int(get_rc(1,1)) num_hst=get_rc(1,2) num_rays=get_rc(1,3) potential=get_rc(2,0) #W potential_err=get_rc(2,1) absorbed=get_rc(3,0) absorbed_err=get_rc(3,1) Fcos=get_rc(4,0) Fcos_err=get_rc(4,1) shadow_loss=get_rc(5,0) shadow_err=get_rc(5,1) missing_loss=get_rc(6,0) missing_err=get_rc(6,1) material_loss=get_rc(7,0) material_err=get_rc(7,1) atmospheric_loss=get_rc(8,0) atmospheric_err=get_rc(8,1) # Target (receiver) # 0 receiver name # 1 - 2 id and area # 3 - 24 (total 22) front # 25- 46 (total 22) back rec_area=0. # m2 rec_front_income=0. rec_front_income_err=0. #rec_no_material_loss=get_rc(num_res+2,5) #rec_no_material_loss_err=get_rc(num_res+2,6) #rec_no_atmo_loss=get_rc(num_res+2,7) #rec_no_atmo_loss_err=get_rc(num_res+2,8) #rec_material_loss=get_rc(num_res+2,9) #rec_material_loss_err=get_rc(num_res+2,10) rec_front_absorbed=0. rec_front_absorbed_err=0. rec_front_eff=0. rec_front_eff_err=0. rec_back_income=0. rec_back_income_err=0. rec_back_absorbed=0. rec_back_absorbed_err=0. rec_back_eff=0. rec_back_eff_err=0. rec_id={} for i in range(num_rec-1): # -1 the virtual target rec_id[i]=get_rc(num_res+2+i,1) # the id number of the receiver rec_area+=get_rc(num_res+2+i,2) # m2 rec_front_income+=get_rc(num_res+2+i,3) rec_front_income_err+=get_rc(num_res+2+i,4) #rec_no_material_loss=get_rc(num_res+2,5) #rec_no_material_loss_err=get_rc(num_res+2,6) #rec_no_atmo_loss=get_rc(num_res+2,7) #rec_no_atmo_loss_err=get_rc(num_res+2,8) #rec_material_loss=get_rc(num_res+2,9) #rec_material_loss_err=get_rc(num_res+2,10) rec_front_absorbed+=get_rc(num_res+2+i,13) rec_front_absorbed_err+=get_rc(num_res+2+i,14) rec_front_eff+=get_rc(num_res+2+i,23) rec_front_eff_err+=get_rc(num_res+2+i,24) rec_back_income+=get_rc(num_res+2+i,25) rec_back_income_err+=get_rc(num_res+2+i,26) rec_back_absorbed+=get_rc(num_res+2+i,35) rec_back_absorbed_err+=get_rc(num_res+2+i,36) rec_back_eff+=get_rc(num_res+2+i,-2) rec_back_eff_err+=get_rc(num_res+2+i,-1) ``` -------------------------------- ### Generate Sun and Atmosphere YAML Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/gen_yaml.html Defines the sun and atmospheric extinction parameters in the YAML output. ```python if spectral: spectrum = "*solar_spectrum" else: spectrum = None iyaml += "- sun: %s\n" % (sun.yaml(spectrum),) if medium>1e-99: iyaml+='- atmosphere: {extinction: %s}\n'%medium iyaml+='\n' ``` -------------------------------- ### Generate YAML Geometry and Entities Source: https://solsticepy.readthedocs.io/en/latest/_modules/solsticepy/gen_yaml.html This snippet demonstrates the iterative process of defining heliostat geometries, pylon structures, and templates, followed by assembling the final scene entities into a YAML file. ```python elif receiver=='multi-aperture': geom, rec_entt, rcv =multi_aperture_receiver(rec_param, hemisphere) iyaml+=geom # # Heliostats Geometry # if one_heliostat: hst_x=np.r_[hst_pos[0]] hst_y=np.r_[hst_pos[1]] hst_z=np.r_[hst_pos[2]] aim_x=np.r_[hst_aims[0]] aim_y=np.r_[hst_aims[1]] aim_z=np.r_[hst_aims[2]] num_hst=1 hst_foc=np.r_[hst_foc] else: hst_x=hst_pos[:,0] hst_y=hst_pos[:,1] hst_z=hst_pos[:,2] aim_x=hst_aims[:,0] aim_y=hst_aims[:,1] aim_z=hst_aims[:,2] num_hst=len(hst_x) slices = 4 # slices for the envelop circle pts_hst = [ [-hst_w*0.5, -hst_h*0.5], [-hst_w*0.5, hst_h*0.5], [hst_w*0.5, hst_h*0.5], [hst_w*0.5,-hst_h*0.5] ] # CREATE a reflective facet (mirror) for i in range(0,num_hst): name_hst_g = 'hst_g_'+str(i) iyaml+='- geometry: &%s\n' % name_hst_g iyaml+=' - material: *%s\n' % 'material_mirror' #iyaml+=' transform: { translation: %s, rotation: %s }\n' % ([hst_x[i], hst_y[i], hst_z[i]], [0, 0, 0]) ) iyaml+=' parabol: \n' iyaml+=' focal: %s\n' % hst_foc[i] iyaml+=' clip: \n' iyaml+=' - operation: AND \n' iyaml+=' vertices: %s\n' % pts_hst iyaml+=' slices: %d\n' % slices # CREATE the pylon "pylon_g" geometry cylindrical shape h_pyl = 0.001 # pylon height r_pyl = 0.2 # pylon radius slices = 4 # slices for the envelop circle iyaml+='- geometry: &%s\n' % 'pylon_g' iyaml+=' - material: *%s\n' % 'material_black' iyaml+=' transform: { translation: %s, rotation: %s }\n' % ([0, 0, -h_pyl*3], [0, 90, 0]) iyaml+=' cylinder: {height: %7.3f, radius: %7.3f, slices: %d }\n' % (h_pyl,r_pyl,slices) # # ### Section (5) # set the templates # (programming objects gathering geometries or pivot and geometries) #------------------------------ # CREATE the heliostat templates for i in range(0,num_hst): name_hst_t = 'hst_t_'+str(i) iyaml+='- template: &%s\n' % name_hst_t name_hst_n = 'hst_'+ str(i) iyaml+=' name: %s\n' % name_hst_n iyaml+=' primary: 0\n' iyaml+=' geometry: *pylon_g\n' iyaml+=' children: \n' iyaml+=' - name: pivot\n' iyaml+=' zx_pivot: {target: {position: %s}} \n' % ([aim_x[i],aim_y[i],aim_z[i]]) iyaml+=' children: \n' iyaml+=' - name: reflect_surface\n' iyaml+=' primary: 1\n' iyaml+=' transform: {rotation: [-90,0,0]} \n' name_hst_g = 'hst_g_'+str(i) iyaml+=' geometry: *%s\n' % name_hst_g # ### Section (6) # set the entities # (gather templates to be created and active in the scene) #------------------------------ # # receiver entities iyaml+=rec_entt # # tower entities iyaml+='\n- entity:\n' iyaml+=' name: tower_e\n' iyaml+=' primary: 0\n' iyaml+=' transform: { translation: %s, rotation: %s }\n' % ([0, -tower_r, tower_h*0.5], [0, 0, 0]) iyaml+=' geometry: *%s\n' % 'tower_g' # # heliostat entities from the template for i in range(0,num_hst): name_e ='H_'+str(i) name_hst_t = 'hst_t_'+str(i) iyaml+='\n- entity:\n' iyaml+=' name: %s\n' % name_e iyaml+=' transform: { translation: %s, rotation: %s }\n' % ([hst_x[i], hst_y[i], hst_z[i]], [0, 0, 0]) iyaml+=' children: [ *%s ]\n' % name_hst_t with open(outfile_yaml,'w') as f: f.write(iyaml) with open(outfile_recv,'w') as f: f.write(rcv) ```