def get_xdot(self, nlp_numerics_options, V, model): """ Get state derivates on all collocation nodes based on polynomials """ scheme = nlp_numerics_options['collocation']['scheme'] Vdot = struct_op.construct_Xdot_struct(nlp_numerics_options, model) # size of the finite elements h = 1. / self.__n_k store_derivatives = [] # collect the derivatives for k in range(self.__n_k): tf = struct_op.calculate_tf(nlp_numerics_options, V, k) # For all collocation points for j in range(self.__d + 1): # get an expression for the state derivative at the collocation point xp_jk = self.__calculate_collocation_deriv(V, k, j) xdot = xp_jk / h / tf store_derivatives = cas.vertcat(store_derivatives, xdot) Xdot = Vdot(store_derivatives) return Xdot
def discretize_constraints(self, options, model, formulation, V, P): """Discretize dynamics and path constraints in a (possibly) parallelizable fashion @param options nlp options @param model awebox model @param formulation awebox formulation @param V decision variables @param P nlp parameters """ # rearrange nlp variables self.__ms_nlp_vars(options, model, V, P) # implicit values of algebraic variables at interval nodes ms_z0 = self.__ms_z0 # evaluate dynamics and constraint functions on all intervals if options['parallelization']['include']: # use function map for parallellization parallellization = options['parallelization']['type'] F_map = self.__F.map('F_map', parallellization, self.__n_k, [], []) path_constraints_fun = model.constraints_fun.map( 'constraints_map', parallellization, self.__n_k, [], []) # notice that these are the model inequality constraints outputs_fun = model.outputs_fun.map('outputs_fun', parallellization, self.__n_k, [], []) # integrate ms_dynamics = F_map(x0=self.__ms_x, z0=self.__ms_z, p=self.__ms_p) ms_xf = ms_dynamics['xf'] ms_qf = cas.horzcat(np.zeros(self.__dae.dae['quad'].size()), ms_dynamics['qf']) ms_constraints = path_constraints_fun( self.__ms_vars, self.__ms_params) # evaluate the model ineqs. at ms_outputs = outputs_fun(self.__ms_vars, self.__ms_params) # integrate quadrature outputs for i in range(self.__n_k): ms_qf[:, i + 1] = ms_qf[:, i + 1] + ms_qf[:, i] else: # initialize function evaluations ms_xf = [] ms_qf = np.zeros(self.__dae.dae['quad'].size()) ms_constraints = [] ms_outputs = [] # evaluate functions in for loop for i in range(self.__n_k): ms_dynamics = self.__F(x0=self.__ms_x[:, i], z0=self.__ms_z[:, i], p=self.__ms_p[:, i]) ms_xf = cas.horzcat(ms_xf, ms_dynamics['xf']) ms_qf = cas.horzcat(ms_qf, ms_qf[:, -1] + ms_dynamics['qf']) ms_constraints = cas.horzcat( ms_constraints, model.constraints_fun(self.__ms_vars[:, i], self.__ms_params[:, i])) ms_outputs = cas.horzcat( ms_outputs, model.outputs_fun(self.__ms_vars[:, i], self.__ms_params[:, i])) # integral outputs and constraints Integral_outputs_list = self.__build_integral_outputs( ms_qf, model.integral_outputs) Integral_constraints_list = None # construct state derivative struct Xdot = struct_op.construct_Xdot_struct(options, model.variables_dict) Xdot = self.__fill_in_Xdot(Xdot) return ms_xf, ms_z0, self.__ms_vars, self.__ms_params, Xdot, ms_constraints, ms_outputs, Integral_outputs_list, Integral_constraints_list
def discretize(nlp_numerics_options, model, formulation): # ----------------------------------------------------------------------------- # discretization setup # ----------------------------------------------------------------------------- nk = nlp_numerics_options['n_k'] if nlp_numerics_options['discretization'] == 'direct_collocation': direct_collocation = True ms = False d = nlp_numerics_options['collocation']['d'] scheme = nlp_numerics_options['collocation']['scheme'] Collocation = collocation.Collocation(nk, d, scheme) Multiple_shooting = None elif nlp_numerics_options['discretization'] == 'multiple_shooting': direct_collocation = False ms = True Collocation = None dae = model.get_dae() Multiple_shooting = multiple_shooting.Multiple_shooting( nk, dae, nlp_numerics_options['integrator']) slacks = None # -------------------------------------- # prepare model variables and dynamics # -------------------------------------- variables = model.variables parameters = model.parameters variables_dict = model.variables_dict #--------------------------------------- # prepare constraints structure #--------------------------------------- form_constraints = formulation.constraints constraints_fun = formulation.constraints_fun # initial, terminal and periodicity constraints path_constraints = model.constraints path_constraints_fun = model.constraints_fun g_struct = constraints.setup_constraint_structure( nlp_numerics_options, model, formulation) # empty struct for collocated constraints #------------------------------------------- # DISCRETIZE VARIABLES, CREATE NLP PARAMETERS #------------------------------------------- V = setup_nlp_v(nlp_numerics_options, model, formulation, Collocation) P = setup_nlp_p(V, model) if direct_collocation: Xdot = Collocation.get_xdot(nlp_numerics_options, V, model) # construct time grids for this nlp time_grids = construct_time_grids(nlp_numerics_options) # --------------------------------------- # prepare outputs structure # --------------------------------------- outputs = model.outputs outputs_fun = model.outputs_fun [form_outputs, form_outputs_dict ] = performance.collect_performance_outputs(nlp_numerics_options, model, V) form_outputs_fun = cas.Function('form_outputs_fun', [V, P], [form_outputs.cat]) Outputs_struct = setup_output_structure(nlp_numerics_options, outputs, form_outputs) #------------------------------------------- # COLLOCATE CONSTRAINTS, OUTPUTS #------------------------------------------- # prepare listing of outputs and constraints Outputs_list = [] g_list = [] g_bounds = {'lb': [], 'ub': []} # extract model.parameters from V param_at_time = parameters(cas.vertcat(P['theta0'], V['phi'])) xi = V['xi'] #TODO: don't hard code! # parallellize constraints on collocation nodes if direct_collocation: [ coll_dynamics, coll_constraints, coll_outputs, Integral_outputs_list, Integral_constraint_list ] = Collocation.collocate_constraints(nlp_numerics_options, model, formulation, V, P, Xdot) # parallellize constraints on interval nodes if ms: [ ms_xf, ms_z0, Xdot, ms_constraints, ms_outputs, Integral_outputs_list, Integral_constraint_list ] = Multiple_shooting.discretize_constraints(nlp_numerics_options, model, formulation, V, P) # Construct list of constraints (+ bounds) and outputs for kdx in range(nk): # time constant of the following interval tf = struct_op.calculate_tf(nlp_numerics_options, V, kdx) if kdx == 0: # extract initial (reference) variables var_initial = struct_op.get_variables_at_time( nlp_numerics_options, V, Xdot, model, 0) var_ref_initial = struct_op.get_var_ref_at_time( nlp_numerics_options, P, V, Xdot, model, 0) # add initial constraints [g_list, g_bounds] = constraints.append_initial_constraints( g_list, g_bounds, form_constraints, constraints_fun, var_initial, var_ref_initial, xi) if (ms) or (direct_collocation and scheme != 'radau'): # at each interval node, algebraic constraints should be satisfied [g_list, g_bounds] = constraints.append_algebraic_constraints( g_list, g_bounds, dae.z(ms_z0[:, kdx]), V, kdx) # at each interval node, path constraints should be satisfied if 'us' in list(V.keys()): # slack path constraints slacks = V['us', kdx] [g_list, g_bounds] = constraints.append_path_constraints( g_list, g_bounds, path_constraints, ms_constraints[:, kdx], slacks) # compute outputs for this time interval Outputs_list.append(ms_outputs[:, kdx]) if direct_collocation: # add constraints and outputs on collocation nodes for ddx in range(d): # at each (except for first node) collocation point dynamics should meet [g_list, g_bounds] = constraints.append_collocation_constraints( g_list, g_bounds, coll_dynamics[:, kdx * d + ddx]) # at each (except for first node) collocation node, path constraints should be satisfied [g_list, g_bounds] = constraints.append_path_constraints( g_list, g_bounds, path_constraints, coll_constraints[:, kdx * d + ddx]) # compute outputs for this time interval Outputs_list.append(coll_outputs[:, kdx * d + ddx]) # endpoint should match next start point [g_list, g_bounds] = Collocation.append_continuity_constraint( g_list, g_bounds, V, kdx) elif ms: # endpoint should match next start point [g_list, g_bounds] = Multiple_shooting.append_continuity_constraint( g_list, g_bounds, ms_xf, V, kdx) # extract terminal (reference) variables var_terminal = struct_op.get_variables_at_final_time( nlp_numerics_options, V, Xdot, model) var_ref_terminal = struct_op.get_var_ref_at_final_time( nlp_numerics_options, P, Xdot, model) # add terminal and periodicity constraints [g_list, g_bounds] = constraints.append_terminal_constraints( g_list, g_bounds, form_constraints, constraints_fun, var_terminal, var_ref_terminal, xi) [g_list, g_bounds] = constraints.append_periodic_constraints( g_list, g_bounds, form_constraints, constraints_fun, var_initial, var_terminal) if direct_collocation: [g_list, g_bounds] = constraints.append_integral_constraints( nlp_numerics_options, g_list, g_bounds, Integral_constraint_list, form_constraints, constraints_fun, V, Xdot, model, formulation.integral_constants) Outputs_list.append(form_outputs_fun(V, P)) # Create Outputs struct and function Outputs = Outputs_struct(cas.vertcat(*Outputs_list)) Outputs_fun = cas.Function('Outputs_fun', [V, P], [Outputs.cat]) # Create Integral outputs struct and function Integral_outputs_struct = setup_integral_output_structure( nlp_numerics_options, model.integral_outputs) Integral_outputs = Integral_outputs_struct( cas.vertcat(*Integral_outputs_list)) Integral_outputs_fun = cas.Function('Integral_outputs_fun', [V, P], [Integral_outputs.cat]) # Create g struct and functions and g_bounds vectors [g, g_fun, g_jacobian_fun, g_bounds] = constraints.create_constraint_outputs(g_list, g_bounds, g_struct, V, P) Xdot_struct = struct_op.construct_Xdot_struct(nlp_numerics_options, model) Xdot_fun = cas.Function('Xdot_fun', [V], [Xdot]) return V, P, Xdot_struct, Xdot_fun, g_struct, g_fun, g_jacobian_fun, g_bounds, Outputs_struct, Outputs_fun, Integral_outputs_struct, Integral_outputs_fun, time_grids, Collocation, Multiple_shooting
def discretize(nlp_options, model, formulation): # ----------------------------------------------------------------------------- # discretization setup # ----------------------------------------------------------------------------- nk = nlp_options['n_k'] direct_collocation = ( nlp_options['discretization'] == 'direct_collocation') multiple_shooting = (nlp_options['discretization'] == 'multiple_shooting') if direct_collocation: d = nlp_options['collocation']['d'] scheme = nlp_options['collocation']['scheme'] Collocation = coll_module.Collocation(nk, d, scheme) dae = None Multiple_shooting = None V = var_struct.setup_nlp_v(nlp_options, model, Collocation) P = setup_nlp_p(V, model) Xdot = Collocation.get_xdot(nlp_options, V, model) [coll_outputs, Integral_outputs_list, Integral_constraint_list ] = Collocation.collocate_outputs_and_integrals( nlp_options, model, formulation, V, P, Xdot) ms_xf = None ms_z0 = None ms_vars = None ms_params = None if multiple_shooting: dae = model.get_dae() Multiple_shooting = ms_module.Multiple_shooting( nk, dae, nlp_options['integrator']) Collocation = None V = var_struct.setup_nlp_v(nlp_options, model) P = setup_nlp_p(V, model) [ ms_xf, ms_z0, ms_vars, ms_params, Xdot, _, ms_outputs, Integral_outputs_list, Integral_constraint_list ] = Multiple_shooting.discretize_constraints(nlp_options, model, formulation, V, P) #------------------------------------------- # DISCRETIZE VARIABLES, CREATE NLP PARAMETERS #------------------------------------------- # construct time grids for this nlp time_grids = construct_time_grids(nlp_options) # --------------------------------------- # PREPARE OUTPUTS STRUCTURE # --------------------------------------- outputs = model.outputs form_outputs, _ = performance.collect_performance_outputs( nlp_options, model, V) form_outputs_fun = cas.Function('form_outputs_fun', [V, P], [form_outputs.cat]) Outputs_struct = setup_output_structure(nlp_options, outputs, form_outputs) #------------------------------------------- # COLLOCATE OUTPUTS #------------------------------------------- # prepare listing of outputs and constraints Outputs_list = [] # Construct outputs if multiple_shooting: for kdx in range(nk): # compute outputs for this time interval Outputs_list.append(ms_outputs[:, kdx]) if direct_collocation: for kdx in range(nk): # add outputs on collocation nodes for ddx in range(d): # compute outputs for this time interval Outputs_list.append(coll_outputs[:, kdx * d + ddx]) Outputs_list.append(form_outputs_fun(V, P)) # Create Outputs struct and function Outputs = Outputs_struct(cas.vertcat(*Outputs_list)) Outputs_fun = cas.Function('Outputs_fun', [V, P], [Outputs.cat]) # Create Integral outputs struct and function Integral_outputs_struct = setup_integral_output_structure( nlp_options, model.integral_outputs) Integral_outputs = Integral_outputs_struct( cas.vertcat(*Integral_outputs_list)) Integral_outputs_fun = cas.Function('Integral_outputs_fun', [V, P], [Integral_outputs.cat]) Xdot_struct = struct_op.construct_Xdot_struct(nlp_options, model.variables_dict) Xdot_fun = cas.Function('Xdot_fun', [V], [Xdot]) # ------------------------------------------- # GET CONSTRAINTS # ------------------------------------------- ocp_cstr_list = constraints.get_constraints(nlp_options, V, P, Xdot, model, dae, formulation, Integral_constraint_list, Collocation, Multiple_shooting, ms_z0, ms_xf, ms_vars, ms_params, Outputs) return V, P, Xdot_struct, Xdot_fun, ocp_cstr_list, Outputs_struct, Outputs_fun, Integral_outputs_struct, Integral_outputs_fun, time_grids, Collocation, Multiple_shooting
def get_strength_constraint(options, V, Outputs, model): n_k = options['n_k'] d = options['collocation']['d'] comparison_labels = options['induction']['comparison_labels'] wake_nodes = options['induction']['vortex_wake_nodes'] rings = wake_nodes - 1 kite_nodes = model.architecture.kite_nodes strength_scale = tools.get_strength_scale(options) Xdot = struct_op.construct_Xdot_struct(options, model.variables_dict)(0.) cstr_list = cstr_op.ConstraintList() any_vor = any(label[:3] == 'vor' for label in comparison_labels) if any_vor: for kite in kite_nodes: for ring in range(rings): wake_node = ring for ndx in range(n_k): for ddx in range(d): local_name = 'vortex_strength_' + str(kite) + '_' + str(ring) + '_' + str(ndx) + '_' + str(ddx) variables = struct_op.get_variables_at_time(options, V, Xdot, model.variables, ndx, ddx) wg_local = tools.get_ring_strength(variables, kite, ring) ndx_shed = n_k - 1 - wake_node ddx_shed = d - 1 # working out: # n_k = 3 # if ndx = 0 and ddx = 0 -> shed: wn >= n_k # wn: 0 sheds at ndx = 2, ddx = -1 : unshed, period = 0 # wn: 1 sheds at ndx = 1, ddx = -1 : unshed, period = 0 # wn: 2 sheds at ndx = 0, ddx = -1 : unshed, period = 0 # wn: 3 sheds at ndx = -1, ddx = -1 : SHED period = 1 # wn: 4 sheds at ndx = -2, period = 1 # wn: 5 sheds at ndx = -3 period = 1 # wn: 6 sheds at ndx = -4 period = 2 # if ndx = 1 and ddx = 0 -> shed: wn >= n_k - ndx # wn: 0 sheds at ndx = 2, ddx = -1 : unshed, # wn: 1 sheds at ndx = 1, ddx = -1 : unshed, # wn: 2 sheds at ndx = 0, ddx = -1 : SHED, # wn: 3 sheds at ndx = -1, ddx = -1 : SHED # if ndx = 0 and ddx = -1 -> shed: # wn: 0 sheds at ndx = 2, ddx = -1 : unshed, # wn: 1 sheds at ndx = 1, ddx = -1 : unshed, # wn: 2 sheds at ndx = 0, ddx = -1 : SHED, # wn: 3 sheds at ndx = -1, ddx = -1 : SHED already_shed = False if (ndx > ndx_shed): already_shed = True elif ((ndx == ndx_shed) and (ddx == ddx_shed)): already_shed = True if already_shed: # working out: # n_k = 3 # period_0 -> wn 0, wn 1, wn 2 -> floor(ndx_shed / n_k) # period_1 -> wn 3, wn 4, wn 5 period_number = int(np.floor(float(ndx_shed)/float(n_k))) ndx_shed_w_periodicity = ndx_shed - period_number * n_k gamma_val = Outputs['coll_outputs', ndx_shed_w_periodicity, ddx_shed, 'aerodynamics', 'circulation' + str(kite)] wg_ref = 1. * gamma_val / strength_scale else: wg_ref = 0. local_resi = (wg_local - wg_ref) local_cstr = cstr_op.Constraint(expr = local_resi, name = local_name, cstr_type='eq') cstr_list.append(local_cstr) return cstr_list
def get_fixing_constraint(options, V, Outputs, model): n_k = options['n_k'] comparison_labels = options['induction']['comparison_labels'] wake_nodes = options['induction']['vortex_wake_nodes'] kite_nodes = model.architecture.kite_nodes wingtips = ['ext', 'int'] Xdot = struct_op.construct_Xdot_struct(options, model.variables_dict)(0.) cstr_list = cstr_op.ConstraintList() any_vor = any(label[:3] == 'vor' for label in comparison_labels) if any_vor: for kite in kite_nodes: for tip in wingtips: for wake_node in range(wake_nodes): local_name = 'wake_fixing_' + str(kite) + '_' + str(tip) + '_' + str(wake_node) if wake_node < n_k: # working out: # n_k = 3 # wn:0, n_k-1=2 # wn:1, n_k-2=1 # wn:2=n_k-1, n_k-3=0 # ... switch to periodic fixing reverse_index = n_k - 1 - wake_node variables_at_shed = struct_op.get_variables_at_time(options, V, Xdot, model.variables, reverse_index, -1) wx_local = tools.get_wake_node_position_si(options, variables_at_shed, kite, tip, wake_node) wingtip_pos = Outputs[ 'coll_outputs', reverse_index, -1, 'aerodynamics', 'wingtip_' + tip + str(kite)] local_resi = wx_local - wingtip_pos local_cstr = cstr_op.Constraint(expr = local_resi, name = local_name, cstr_type='eq') cstr_list.append(local_cstr) else: # working out: # n_k = 3 # wn:0, n_k-1=2 # wn:1, n_k-2=1 # wn:2=n_k-1, n_k-3=0 # ... switch to periodic fixing # wn:3 at ndx = 0 must be equal to -> wn:0 at ndx = -1, ddx = -1 # wn:4 at ndx = 0 must be equal to -> wn:1 at ndx = -1, ddx = -1 variables_at_initial = struct_op.get_variables_at_time(options, V, Xdot, model.variables, 0) variables_at_final = struct_op.get_variables_at_time(options, V, Xdot, model.variables, -1, -1) upstream_node = wake_node - n_k wx_local = tools.get_wake_node_position_si(options, variables_at_initial, kite, tip, wake_node) wx_upstream = tools.get_wake_node_position_si(options, variables_at_final, kite, tip, upstream_node) local_resi = wx_local - wx_upstream local_cstr = cstr_op.Constraint(expr = local_resi, name = local_name, cstr_type='eq') cstr_list.append(local_cstr) return cstr_list
def discretize_constraints(self, options, model, formulation, V, P): """Discretize dynamics and path constraints in a (possibly) parallelizable fashion @param options nlp options @param model awebox model @param formulation awebox formulation @param V decision variables @param P nlp parameters """ # rearrange nlp variables self.__ms_nlp_vars(options, model, V, P) # implicit values ofalgebraic variables at interval nodes ms_z0 = self.__ms_z0 # evaluate dynamics and constraint functions on all intervals if options['parallelization']['include']: # use function map for parallellization parallellization = options['parallelization']['type'] F_map = self.__F.map('F_map', parallellization, self.__n_k, [], []) path_constraints_fun = model.constraints_fun.map( 'constraints_map', parallellization, self.__n_k, [], []) outputs_fun = model.outputs_fun.map('outputs_fun', parallellization, self.__n_k, [], []) # integrate ms_dynamics = F_map(x0=self.__ms_x, z0=self.__ms_z, p=self.__ms_p) ms_xf = ms_dynamics['xf'] ms_qf = cas.horzcat(np.zeros(self.__dae.dae['quad'].size()), ms_dynamics['qf']) ms_constraints = path_constraints_fun(self.__ms_vars, self.__ms_params) ms_outputs = outputs_fun(self.__ms_vars, self.__ms_params) # integrate quadrature outputs for i in range(self.__n_k): ms_qf[:, i + 1] = ms_qf[:, i + 1] + ms_qf[:, i] # extract formulation information # constraints_fun_ineq = formulation.constraints_fun['integral']['inequality'].map('integral_constraints_map_ineq', 'serial', N_coll, [], []) # constraints_fun_eq = formulation.constraints_fun['integral']['equality'].map('integral_constraints_map_eq', 'serial', N_coll, [], []) # integral_constraints = OrderedDict() # integral_constraints['inequality'] = constraints_fun_ineq(coll_vars, coll_params) # integral_constraints['equality'] = constraints_fun_eq(coll_vars, coll_params) else: # initialize function evaluations ms_xf = [] ms_qf = np.zeros(self.__dae.dae['quad'].size()) ms_constraints = [] ms_outputs = [] # integral_constraints = OrderedDict() # integral_constraints['inequality'] = [] # integral_constraints['equality'] = [] # evaluate functions in for loop for i in range(self.__n_k): ms_dynamics = self.__F(x0=self.__ms_x[:, i], z0=self.__ms_z[:, i], p=self.__ms_p[:, i]) ms_xf = cas.horzcat(ms_xf, ms_dynamics['xf']) ms_qf = cas.horzcat(ms_qf, ms_qf[:, -1] + ms_dynamics['qf']) ms_constraints = cas.horzcat( ms_constraints, model.constraints_fun(self.__ms_vars[:, i], self.__ms_params[:, i])) ms_outputs = cas.horzcat( ms_outputs, model.outputs_fun(self.__ms_vars[:, i], self.__ms_params[:, i])) # integral_constraints['inequality'] = cas.horzcat(integral_constraints['inequality'], formulation.constraints_fun['integral']['inequality'](coll_vars[:,i],coll_params[:,i])) # integral_constraints['equality'] = cas.horzcat(integral_constraints['equality'], formulation.constraints_fun['integral']['equality'](coll_vars[:,i],coll_params[:,i])) # integral outputs and constraints Integral_outputs_list = self.__build_integral_outputs( ms_qf, model.integral_outputs) # Integral_constraints_list = [] # for kdx in range(self.__n_k): # tf = struct_op.calculate_tf(options, V, kdx) # Integral_constraints_list += [self.__integrate_integral_constraints(integral_constraints, kdx, tf)] Integral_constraints_list = None # construct state derivative struct Xdot = struct_op.construct_Xdot_struct(options, model) Xdot = self.__fill_in_Xdot(Xdot) return ms_xf, ms_z0, Xdot, ms_constraints, ms_outputs, Integral_outputs_list, Integral_constraints_list