def test_WP2input_tidal_init(tidalsite, tidal, tidal_kwargs): site = WP2_SiteData(*tidalsite) machine = WP2_MachineData(*tidal, **tidal_kwargs) WP2input(machine, site) assert True
def test_WP2input_wavebiggamma_init(wavesitebiggamma, wave, wave_data_folder): site = WP2_SiteData(*wavesitebiggamma) machine = WP2_MachineData(*wave, wave_data_folder=wave_data_folder) WP2input(machine, site) assert True
def test_WP2_init_wave(wavesite, wave, wave_data_folder): site = WP2_SiteData(*wavesite) machine = WP2_MachineData(*wave, wave_data_folder=wave_data_folder) test = WP2input(machine, site) WP2(test) assert True
def test_WP2_optimisationLoop_tidal(tidalsite, tidal, tidal_kwargs): site = WP2_SiteData(*tidalsite) machine = WP2_MachineData(*tidal, **tidal_kwargs) data = WP2input(machine, site) test = WP2(data) result = test.optimisationLoop() assert result
def test_WP2_optimisationLoop_wave(wavesite, wave, wave_data_folder): site = WP2_SiteData(*wavesite) machine = WP2_MachineData(*wave, wave_data_folder=wave_data_folder) data = WP2input(machine, site) test = WP2(data) result = test.optimisationLoop() assert result
#The input folder defined the place were the software will search for #the user tidal database or the WAMIT folder or the WP2Input.wp2 file or the iWP2Pickler.pkl file #InputFolder = 'C:\\Users\\fferri\\Desktop\\dtocean_tidal' #DATAfolder = (InternalModel_flag,InputFolder) UserOutputTable = None Machine = WP2_MachineData(Type, lCS, Clen, YawAngle, Float_flag, InstalDepth, MinDist, OpThreshold, UserArray, RatedPowerArray, RatedPowerDevice, UserOutputTable, "", tidal_power_curve=Cp, tidal_thrust_curve=Ct, tidal_bidirectional=Bidirection, tidal_cutinout=C_IO, tidal_velocity_curve=X) " Input assembly " iWP2input = WP2input(Machine,Site) if not iWP2input.stopWP2run: WPobj = WP2(iWP2input,debug=False) #Out = WPobj.optimisationLoop(FixedLayOut=FixedArrayLayout)
UserArray = {'Option': 1, 'Value': 'rectangular'} RatedPowerArray = 20 RatedPowerDevice = 1 #This will trigger the WAMIT or Nemoh calculation for the wave case #and it is used in the tidal case for check on the user database format #compability InternalModel_flag = True #The input folder defined the place were the software will search for #the user tidal database or the WAMIT folder or the WP2Input.wp2 file or the iWP2Pickler.pkl file InputFolder = '' DATAfolder = (InternalModel_flag, InputFolder) UserOutputTable = None Machine = WP2_MachineData(Type, lCS, Clen, YawAngle, SingleMachine, Float_flag, InstalDepth, MinDist, OpThreshold, DATAfolder, UserArray, RatedPowerArray, RatedPowerDevice, UserOutputTable) " Input assembly " iWP2input = WP2input(Machine, Site) if not iWP2input.stopWP2run: WPobj = WP2(iWP2input, debug=True) #Out = WPobj.optimisationLoop(FixedLayOut=FixedArrayLayout) Out = WPobj.optimisationLoop() Out.printRes()
def connect(self, debug_entry=False, export_data=True): '''The connect method is used to execute the external program and populate the interface data store with values. Note: Collecting data from the interface for use in the external program can be accessed using self.data.my_input_variable. To put new values into the interface once the program has run we set self.data.my_output_variable = value ''' # #------------------------------------------------------------------------------ # #------------------------------------------------------------------------------ # #------------------ WP2 site data class # #------------------------------------------------------------------------------ # #------------------------------------------------------------------------------ # Sitedata class: The class contains all the information relative to the area of deployment # of the array. # # Args: # LeaseArea (numpy.ndarray) [m]: UTM coordinates of the lease area poligon expressed as [X,Y]. # X is the column vector containing the easting coordinates # Y is the column vector containing the northing coordinates # NogoAreas (list) [m]: list containing the UTM coordinates of the nogo areas poligons expressed as [X,Y]. # MeteoceanConditions (dict): dictionary gathering all the information related to the metocean conditions # of the site. The dictionary is different for wave and tidal cases: # Wave keys: # 'Te' (numpy.ndarray)[s]: Vector containing the wave energy periods # 'Hs' (numpy.ndarray)[m]: Vector containing the significant wave height # 'dir' (numpy.ndarray)[rad]: Vector containing the wave direction # 'p' (numpy.ndarray)[-]: Probability of occurence of the sea state # 'specType' (tup): description of the spectral shape recorded at the site # (spectrum name, gamma factor, spreading parameter) # 'SSH' (float)[m]: Sea Surface Height wrt the bathymetry datum at a single point # Tidal keys: # 'V' (numpy.ndarray)[m/s]: northing-direction of the velocity field # 'U' (numpy.ndarray)[m/s]: easting-direction of the velocity field # 'p' (numpy.ndarray)[-]: probability of occurency of the state # 'TI' (numpy.ndarray)[-]: Turbulence intensity. It can be a single float or a matrix where the # TI is specified at each grid node. # 'x' (numpy.ndarray)[m]: Vector containing the easting coordinate of the grid nodes # 'y' (numpy.ndarray)[m]: Vector containing the northing coordinate of the grid nodes # 'SSH' (numpy.ndarray)[m]: Sea Surface Height wrt the bathymetry datum # # # VelocityShear (numpy.ndarray) [-]: Tidal velocity shear formula (power law), used to evaluate the vertical velocity profile # Main_Direction (numpy.ndarray, optional) [m]: xy vector defining the main orientation of the array. If not provided it will be # assessed from the Metocean conditions. # Bathymetry (numpy.ndarray) [m]: Describes the vertical profile of the sea bottom at each (given) UTM coordinate. # Expressed as [X,Y,Z] # Geophysics (numpy.ndarray) [?]: Describes the sea bottom geophysic characteristic at each (given) UTM coordinate. # Expressed as [X,Y,Geo] # BR (float) [-]: describes the ratio between the lease area surface over the site area surface enclosed in a channel. # 1. - closed channel # 0. - open sea # electrical_connection_point (numpy.ndarray) [m]: UTM coordinates of the electrical connection point at the shore line # boundary_padding (float, optional) [m]: Padding added to inside of the lease area in which devices may not be placed # Check whether the bin width divides the RP perfectly check_bin_widths(self.data.rated_power_device, self.data.pow_bins) if 'Tidal' in self.data.type: x = self.data.tidal_series.coords["UTM x"] y = self.data.tidal_series.coords["UTM y"] tide_dict = { "U": self.data.tidal_series.U.values, "V": self.data.tidal_series.V.values, "SSH": self.data.tidal_series.SSH.values, "TI": self.data.tidal_series.TI.values, "x": x.values, "y": y.values, "t": self.data.tidal_series.t.values, "xc": self.data.tidal_occurrence_point.x, "yc": self.data.tidal_occurrence_point.y, "ns": self.data.tidal_nbins } occurrence_matrix = make_tide_statistics(tide_dict) p_total = sum(occurrence_matrix['p']) if not np.isclose(p_total, 1.): errStr = ("Tidal statistics probabilities invalid. Total " "probability equals {}").format(p_total) raise ValueError(errStr) occurrence_matrix_coords = [ occurrence_matrix['x'], occurrence_matrix['y'], occurrence_matrix['p'] ] matrix_xset = { "values": { "U": occurrence_matrix['U'], "V": occurrence_matrix['V'], "SSH": occurrence_matrix['SSH'], "TI": occurrence_matrix['TI'] }, "coords": occurrence_matrix_coords } self.data.tidal_occurrence = matrix_xset x = self.data.geophysics.coords["UTM x"] y = self.data.geophysics.coords["UTM y"] # Flatten mannings number xgrid, ygrid = np.meshgrid(x.values, y.values) geogrid = self.data.geophysics.values.T geoflat = np.array( zip(xgrid.flatten(), ygrid.flatten(), geogrid.flatten())) else: occurrence_matrix = make_wave_statistics(self.data.wave_series) p_total = occurrence_matrix['p'].sum() if not np.isclose(p_total, 1.): errStr = ("Wave statistics probabilities invalid. Total " "probability equals {}").format(p_total) raise ValueError(errStr) occurrence_matrix_coords = [ occurrence_matrix['Te'], occurrence_matrix['Hs'], occurrence_matrix['B'] ] matrix_xgrid = { "values": occurrence_matrix['p'], "coords": occurrence_matrix_coords } self.data.wave_occurrence = matrix_xgrid # Translate spectrum type spectrum_map = { "Regular": "Regular", "Pierson-Moskowitz": "Pierson_Moskowitz", "JONSWAP": "Jonswap", "Bretschneider": "Bretschneider_Mitsuyasu", "Modified Bretschneider": "Modified_Bretschneider_Mitsuyasu" } spectrum_type = spectrum_map[self.data.spectrum_type_farm] spectrum_list = (spectrum_type, self.data.spectrum_gamma_farm, self.data.spectrum_dir_spreading_farm) occurrence_matrix["SSH"] = 0. # Datum is mean sea level occurrence_matrix["specType"] = spectrum_list geoflat = None # Snap lease area to bathymetry bathy_x = self.data.bathymetry["x"] bathy_y = self.data.bathymetry["y"] bathy_box = box(bathy_x.min(), bathy_y.min(), bathy_x.max(), bathy_y.max()) lease_area = self.data.lease_area sane_lease_area = lease_area.intersection(bathy_box) # Convert lease and nogo polygons numpy_lease = np.array(sane_lease_area.exterior.coords[:-1]) if self.data.nogo_areas is None: numpy_nogo = None else: numpy_nogo = [ np.array(x.exterior.coords[:-1]) for x in self.data.nogo_areas.values() ] numpy_landing = np.array(self.data.export_landing_point.coords[0]) # Bathymetry (**assume layer 1 in uppermost**) zv = self.data.bathymetry["depth"].sel(layer="layer 1").values.T xv, yv = np.meshgrid(self.data.bathymetry["x"].values, self.data.bathymetry["y"].values) xyz = np.dstack([xv.flatten(), yv.flatten(), zv.flatten()])[0] safe_xyz = xyz[~np.isnan(xyz).any(axis=1)] # Convert main direction to vector if self.data.main_direction is None: main_direction_vec = None else: main_direction_tuple = bearing_to_vector(self.data.main_direction) main_direction_vec = np.array(main_direction_tuple) Site = WP2_SiteData(numpy_lease, numpy_nogo, occurrence_matrix, 7., main_direction_vec, safe_xyz, geoflat, self.data.blockage_ratio, numpy_landing, self.data.boundary_padding) # #------------------------------------------------------------------------------ # #------------------------------------------------------------------------------ # #------------------ WP2 machine data class # #------------------------------------------------------------------------------ # #------------------------------------------------------------------------------ # # MachineData class: The class contains all the information relative to the machine # deployed in the array. # # Args: # Type (str)[-]: defines the type of device either 'tidal' or 'wave'. No other strings are accepted # lCS (numpy.ndarray)[m]: position vector of the local coordina system from the given reference point. # Wave: represents the position vector of the body CS wrt the mesh CS # Tidal: represents the position of the hub from the machine (reference) CS. # Clen (numpy.ndarray)[m]: characteristic lenght of the device: # Wave: unused # Tidal: turbine diameter and distance of the hub from the center line # used in case the machine is composed by two parallel turbines # YawAngle (float)[rad]: Yaw angle span, wrt the main direction of the array. # The total yawing range is two-fold the span. -Yaw/+Yaw # Float_flag (bool)[-]: defines whether the machine is floating (True) or not (False) # InstalDepth (list)[m]: defines the min and maximum water depth at which the device can be installed # MinDist (tuple)[m]: defines the minimum allowed distance between devices in the array configuration # the first element is the distance in the x axis # the second element is the distance in the y axis # OpThreshold (float)[-]: defines the minimum allowed q-facto # UserArray (dict): dictionary containing the description of the array layout to be optimise. Keys: # 'Option' (int): 1-optimisation over the internal parametric array layouts # 2-fixed array layout specified by the user not subject to optimisation # 3-array layout specified by the user subject to optimisation via expantion of the array # 'Value' options: # (str) 'rectangular' # (str) 'staggered' # (str) 'full' # (numpy.ndarray) [m]: [X,Y] coordiantes of the device # RatedPowerArray (float)[W]: Rated power of the array. # RatedPowerDevice (float)[W]: Rated power of the single isolated device. # UserOutputTable (dict, optional): dictionary of dictionaries where all the array layouts inputed and analysed by the user are # collected. Using this option the internal WP2 calculation is skipped, and the optimisaton # is performed in the given data. The dictionaies keys are the arguments of the WP2 Output class. # wave_data_folder (string, optional): path name of the hydrodynamic results generate by the wave external module # tidal_power_curve (numpy.ndarray, optional)[-]: Power curve function of the stream velocity # tidal_thrust_curve (numpy.ndarray, optional)[-]: Thrust curve function of the stream velocity # tidal_velocity_curve (numpy.ndarray, optional)[m/s]: Vector containing the stream velocity # tidal_cutinout (numpy.ndarray, optional): contain the cut_in and cut_out velocity of the turbine. # Outside the cut IN/OUT velocity range the machine will not produce # power. The generator is shut down, but the machine will still interact # with the others. # tidal_bidirectional (bool, optional): bidirectional working principle of the turbine # tidal_data_folder (string, optional): Path to tidal device CFD data files yaw_angle = np.radians(self.data.yaw_angle) min_install = self.data.min_install max_install = self.data.max_install min_dist = (self.data.min_dist_x, self.data.min_dist_y) op_threshold = self.data.op_threshold install_depth = (min_install, max_install) if 'Tidal' in self.data.type: perf_velocity = self.data.perf_curves.index.values cp_curve = self.data.perf_curves["Coefficient of Power"].values ct_curve = self.data.perf_curves["Coefficient of Thrust"].values cut_in = self.data.cut_in cut_out = self.data.cut_out dev_type = "Tidal" lCS = [0, 0, self.data.hub_height] clen = (self.data.rotor_diam, self.data.turbine_interdist) wave_data_folder = None tidal_power_curve = cp_curve tidal_thrust_curve = ct_curve tidal_velocity_curve = perf_velocity tidal_cutinout = (cut_in, cut_out) tidal_bidirectional = self.data.bidirection tidal_data_folder = self.data.tidal_data_directory else: dev_type = "Wave" lCS = None clen = None wave_data_folder = self.data.wave_data_directory tidal_power_curve = None tidal_thrust_curve = None tidal_velocity_curve = None tidal_cutinout = None tidal_bidirectional = None tidal_data_folder = None # Set user_array_dict value key if self.data.user_array_option in [ "User Defined Flexible", "User Defined Fixed" ]: if self.data.user_array_layout is None: errStr = ("A predefined array layout must be provided when " "using the '{}' array layout option").format( self.data.user_array_option) raise ValueError(errStr) numpy_layout = np.array( [point.coords[0][:2] for point in self.data.user_array_layout]) user_array_dict = {'Value': numpy_layout} else: user_array_dict = {'Value': self.data.user_array_option.lower()} # Set user_array_dict option key if self.data.user_array_option == "User Defined Flexible": user_array_dict['Option'] = 3 elif self.data.user_array_option == "User Defined Fixed": user_array_dict['Option'] = 2 else: user_array_dict['Option'] = 1 if 'Floating' in self.data.type: float_flag = True else: float_flag = False Machine = WP2_MachineData( dev_type, lCS, clen, yaw_angle, float_flag, install_depth, min_dist, op_threshold, user_array_dict, self.data.rated_array_power * 1e6, # Watts self.data.rated_power_device * 1e6, # Watts None, wave_data_folder, tidal_power_curve, tidal_thrust_curve, tidal_velocity_curve, tidal_cutinout, tidal_bidirectional, tidal_data_folder) iWP2input = WP2input(Machine, Site) if export_data: userdir = UserDataDirectory("dtocean_core", "DTOcean", "config") if userdir.isfile("files.ini"): configdir = userdir else: configdir = ObjDirectory("dtocean_core", "config") files_ini = ReadINI(configdir, "files.ini") files_config = files_ini.get_config() appdir_path = userdir.get_path("..") debug_folder = files_config["debug"]["path"] debug_path = os.path.join(appdir_path, debug_folder) debugdir = Directory(debug_path) debugdir.makedir() pkl_path = debugdir.get_path("hydrodynamics_inputs.pkl") pickle.dump(iWP2input, open(pkl_path, "wb")) if debug_entry: return if not iWP2input.stopWP2run: main = WP2(iWP2input, pickup=True, debug=False) result = main.optimisationLoop() if result == -1: errStr = "Hydrodynamics module failed to execute successfully." raise RuntimeError(errStr) if export_data: pkl_path = debugdir.get_path("hydrodynamics_outputs.pkl") pickle.dump(result, open(pkl_path, "wb")) AEP_per_device = {} pow_per_device = {} pmf_per_device = {} layout = {} q_factor_per_device = {} dev_ids = [] # Layout for dev_id, coords in result.Array_layout.iteritems(): dev_id = dev_id.lower() layout[dev_id] = np.array(coords) dev_ids.append(dev_id) self.data.device_position = layout self.data.n_bodies = int(result.Nbodies) # Total annual energy (convert to MWh) self.data.AEP_array = \ float(result.Annual_Energy_Production_Array) / 1e6 # Array capacity factor ideal_energy = (365 * 24 * self.data.n_bodies * self.data.rated_power_device) self.data.array_efficiency = self.data.AEP_array / ideal_energy # Annual energy per device (convert to MWh) for dev_id, AEP in zip(dev_ids, result.Annual_Energy_Production_perD): AEP_per_device[dev_id] = float(AEP) / 1e6 #SimpleDIct self.data.AEP_per_device = AEP_per_device # Mean power per device (convert to MW) for dev_id, power in zip(dev_ids, result.power_prod_perD): pow_per_device[dev_id] = float(power) / 1e6 #SimpleDIct self.data.pow_per_device = pow_per_device for dev_id, pow_per_state in zip(dev_ids, result.power_prod_perD_perS): # Power probability mass function (convert to MW) flat_prob = occurrence_matrix['p'].flatten("F") pow_list = pow_per_state / 1e6 assert np.isclose(flat_prob.sum(), 1.) assert len(flat_prob) == len(pow_list) # Find uniques powers unique_powers = [] for power in pow_list: if not np.isclose(power, unique_powers).any(): unique_powers.append(power) # Catch any matching powers and sum the probabilities powers = [] probs = [] match_index_check = [] for power in unique_powers: matches = np.isclose(power, pow_list) assert len(matches) >= 1 match_idx = np.where(matches == True) match_probs = flat_prob[match_idx] match_index_check.extend(match_idx[0].tolist()) powers.append(power) probs.append(match_probs.sum()) # Nullify the found indexes to ensure uniqueness pow_list[match_idx] = np.nan flat_prob[match_idx] = np.nan repeated_indexes = set([ x for x in match_index_check if match_index_check.count(x) > 1 ]) assert len(repeated_indexes) == 0 assert np.isclose(sum(probs), 1.) pmf_per_device[dev_id] = np.array(zip(powers, probs)) # Power probability histograms dev_pow_hists = make_power_histograms(pmf_per_device, self.data.rated_power_device, self.data.pow_bins) self.data.pow_pmf_per_device = pmf_per_device self.data.pow_hist_per_device = dev_pow_hists # Resource modification self.data.q_factor_per_device = q_factor_per_device self.data.q_factor_array = result.q_factor_Array self.data.resource_reduction = float(result.Resource_Reduction) for dev_id, q_factor in zip(dev_ids, result.q_factor_Per_Device): q_factor_per_device[dev_id] = q_factor # Main Direction self.data.main_direction = vector_to_bearing(*result.main_direction) # Device type specific outputs if 'Wave' in self.data.type: # External forces fex_dict = result.Hydrodynamic_Parameters modes = np.array(fex_dict["mode_def"]) freqs = np.array(fex_dict["wave_fr"]) # Convert directions to bearings bearings = [radians_to_bearing(x) for x in fex_dict["wave_dir"]] dirs = np.array(bearings) fex_raw = np.zeros( [len(modes), len(freqs), len(dirs)], dtype=complex) * np.nan for i, mode_fex in enumerate(fex_dict["fex"]): if mode_fex: fex_raw[i, :, :] = np.expand_dims(mode_fex, axis=1) fex_xgrid = {"values": fex_raw, "coords": [modes, freqs, dirs]} self.data.ext_forces = fex_xgrid ## Power Matrix in kW power_matrix = result.power_matrix_machine / 1000. power_matrix_dims = result.power_matrix_dims # Convert directions to bearings bearings = [ radians_to_bearing(x) for x in power_matrix_dims["dirs"] ] occurrence_matrix_coords = [ power_matrix_dims['te'], power_matrix_dims['hm0'], bearings ] matrix_xgrid = { "values": power_matrix, "coords": occurrence_matrix_coords } self.data.power_matrix = matrix_xgrid return
#UserArray = {'Option':1,'Value':'full'} UserArray = {'Option':2,'Value':FixedArrayLayout} #UserArray = {'Option':3,'Value':np.random.rand(20,2)*200} RatedPowerArray = 12 RatedPowerDevice = 6 UserOutputTable = None Machine = WP2_MachineData(Type, lCS, Clen, YawAngle, Float_flag, InstalDepth, MinDist, OpThreshold, UserArray, RatedPowerArray, RatedPowerDevice, wave_data_folder=InputFolder) " Input assembly " #Site.printInput(indent=1) #help(Site) iWP2input = WP2input(Machine,Site) if not iWP2input.stopWP2run: WPobj = WP2(iWP2input,
def test_WP2_MachineData_init(tidal): WP2_MachineData(*tidal) assert True
z = np.empty((1)) c_io = np.array([cut_in, cut_out]) single_machine_description = { 'pf': Cp_curve, 'Lf2': bidirection, 'Lf1': Ct_curve, 'x': performance_velocity, 'y': y, 'z': z, 'Add': c_io } Machine = WP2_MachineData('tidal', lCS, (rotor_diam, turbine_interdist), yaw_angle, single_machine_description, float_flag, (min_install, max_install), (min_dist_x, min_dist_y), op_threshold, { 'Option': 1, 'Value': user_array_option }, rated_array_power, rated_power_device, user_array_layout) " Input assembly " iWP2input = WP2input(Machine, Site) if not iWP2input.stopWP2run: WPobj = WP2(iWP2input, debug=True) #Out = WPobj.optimisationLoop(FixedLayOut=FixedArrayLayout) Out = WPobj.optimisationLoop() Out.printRes()