def extract_all_info(self, general_inputs, liquid_properties): """ Extract all the important data generated from the electrostatics simulation. Args: general_inputs: Object containing the SimulationGeneralParameters class. liquid_properties: Object obtained from the Liquid_Parameters class, which is located at Liquids.py Returns: """ self.potential = self.class_caller.phi self.vacuum_electric_field = self.class_caller.E_v # Vacuum electric field. self.normal_component_vacuum = self.class_caller.E_v_n # Normal component of the electric field @interface self.tangential_component = self.class_caller.E_t # @interface self.surface_charge_density = self.class_caller.sigma # Surface charge density at interface. # Get coordinates of the nodes and midpoints. r_nodes, z_nodes = general_inputs.get_nodepoints(self.mesh, self.boundaries, self.boundaries_ids['Interface']) self.coords_nodes = [r_nodes, z_nodes] r_mids, z_mids = general_inputs.get_midpoints(self.boundaries, self.boundaries_ids['Interface']) self.coords_mids = [r_mids, z_mids] # Split the electric field into radial and axial components. self.radial_component_vacuum, self.axial_component_vacuum = \ PostProcessing.extract_from_function(self.vacuum_electric_field, self.coords_nodes) # E_v_n_array = PostProcessing.extract_from_function(Electrostatics.normal_component_vacuum, coords_mids) E_t_array = PostProcessing.extract_from_function(self.tangential_component, self.coords_mids) # Define an auxiliary term for the computations. K = 1 + general_inputs.Lambda * (general_inputs.T_h - 1) self.normal_component_liquid = (self.normal_component_vacuum - self.surface_charge_density) / \ liquid_properties.eps_r E_l_n_array = PostProcessing.extract_from_function(self.normal_component_liquid, self.coords_mids) # Get components of the liquid field. self.radial_component_liquid, self.axial_component_liquid = \ Poisson.get_liquid_electric_field(mesh=self.mesh, subdomain_data=self.boundaries, boundary_id=self.boundaries_ids['Interface'], normal_liquid=E_l_n_array, tangential_liquid=E_t_array) self.radial_component_liquid.append(self.radial_component_liquid[-1]) self.axial_component_liquid.append(self.axial_component_liquid[-1]) # Calculate the non-dimensional evaporated charge and current. self.evaporated_charge = (self.surface_charge_density * general_inputs.T_h) / (liquid_properties.eps_r * general_inputs.Chi) \ * fn.exp(-general_inputs.Phi / general_inputs.T_h * (1 - pow(general_inputs.B, 1 / 4) * fn.sqrt( self.normal_component_vacuum)) ) self.conducted_charge = K * self.normal_component_liquid # Calculate the emitted current through the interface. self.emitted_current = self.class_caller.get_nd_current(self.evaporated_charge) # Compute the normal component of the electric stress at the meniscus (electric pressure). self.normal_electric_stress = (self.normal_component_vacuum ** 2 - liquid_properties.eps_r * self.normal_component_liquid ** 2) + \ (liquid_properties.eps_r - 1) * self.tangential_component ** 2
def extract_all_info(self, general_inputs): """ Extract all possible information from the Stokes simulation. Args: general_inputs: Class containing inputs which are general for all simulations. Object containing the SimulationGeneralParameters class. Returns: """ self.r_nodes, self.z_nodes = general_inputs.r_nodes, general_inputs.z_nodes self.coords_nodes = [self.r_nodes, self.z_nodes] self.r_mids, self.z_mids = general_inputs.r_mids, general_inputs.z_mids self.coords_mids = [self.r_mids, self.z_mids] self.velocity_field = self.class_caller.u self.radial_component_velocity, self.axial_component_velocity = PostProcessing.extract_from_function( self.velocity_field, self.coords_nodes) self.normal_velocity = self.class_caller.u_n self.tangential_velocity = self.class_caller.u_t self.pressure = self.class_caller.p_star - general_inputs.P_r + \ self.electrostatics_simulation.emitted_current*general_inputs.C_R self.normal_hydraulic_stress = \ self.class_caller.block_project(self.class_caller.theta,self.electrostatics_simulation.mesh, self.electrostatics_simulation.restrictions_dict['interface_rtc'], self.electrostatics_simulation.boundaries, self.electrostatics_simulation.boundaries_ids['Interface'], space_type='scalar', boundary_type='internal') self.convection_charge = self.class_caller.j_conv
def plot_results(self, save_images=False, save_mat=False, **kwargs): """ Plot the results obtained from the Stokes simulation. Args: save_images: Boolean indicating if images should be saved or not. save_mat: Boolean indicating if .mat files from the plotted data should be saved or not. kwargs: All kwargs accepted by SciencePlotting. See its docs for a full reference. Returns: """ # Obtain the arrays from the FEniCS functions. p_arr = PostProcessing.extract_from_function(self.pressure, self.coords_nodes) n_tauh_n_arr = PostProcessing.extract_from_function(self.normal_hydraulic_stress, self.coords_mids) j_conv_arr = PostProcessing.extract_from_function(self.convection_charge, self.coords_nodes) u_n_arr = PostProcessing.extract_from_function(self.normal_velocity, self.coords_mids) u_t_arr = PostProcessing.extract_from_function(self.tangential_velocity, self.coords_mids) # Apply the user's inputs into the plotting class. kwargs.setdefault('save_fig', save_images) kwargs.setdefault('save_mat', save_mat) # Plot the results. self.spy.lineplot([(self.r_nodes, self.radial_component_velocity, r'Radial ($\hat{r}$)'), (self.r_nodes, self.axial_component_velocity, r'Axial ($\hat{z}$)')], xlabel=r'$\hat{r}$', ylabel=r'$\hat{u}$', fig_title='Components of the velocity field', **kwargs) self.spy.lineplot({r'$\hat{r}$': self.r_nodes, r'$\hat{p}$': p_arr}, fig_title='Pressure along the meniscus', **kwargs) self.spy.lineplot({r'$\hat{r}$': self.r_mids, r'$\hat{u}_n$': u_n_arr}, fig_title='Normal Component of the velocity field.', **kwargs) self.spy.lineplot({r'$\hat{r}$': self.r_mids, r'$\hat{u}_t$': u_t_arr}, fig_title='Tangential Component of the velocity field.', **kwargs) self.spy.lineplot({r'$\hat{r}$': self.r_nodes, r'$\hat{j}_{conv}$': j_conv_arr}, fig_title='Convection charge transport', **kwargs) self.spy.lineplot({r'$\hat{r}$': self.r_mids, r'$\mathbf{n}\cdot\hat{\bar{\bar{\tau}}}_m \cdot \mathbf{n}$': n_tauh_n_arr}, fig_title='Normal component of the hydraulic stress at the meniscus', **kwargs)
def plot_results(self, save_images=False, save_mat=False, **kwargs): """ Plot the most important data obtained from the Electrostatics simulation. Args: save_images: Boolean indicating if images must be saved or not. save_mat: Boolean indicating if data must be saved into .mat files. kwargs: All kwargs accepted are from the lineplot method of SciencePlotting class. Check its docs for full reference. Returns: """ # Extract the information from FEniCS functions to numpy arrays. E_v_n_array = PostProcessing.extract_from_function(self.normal_component_vacuum, self.coords_mids) E_l_n = (self.normal_component_vacuum - self.surface_charge_density) / self.liquid_properties.eps_r E_l_n_array = PostProcessing.extract_from_function(E_l_n, self.coords_mids) E_t_array = PostProcessing.extract_from_function(self.tangential_component, self.coords_mids) sigma_arr = PostProcessing.extract_from_function(self.surface_charge_density, self.coords_mids) j_ev_arr = PostProcessing.extract_from_function(self.evaporated_charge, self.coords_mids) n_taue_n_arr = PostProcessing.extract_from_function(self.normal_electric_stress, self.coords_mids) # Define generic labels. x_label = r'$\hat{r}$' y_label = r'$\hat{E}$' # Setup input for the plotting function. kwargs.setdefault('save_fig', save_images) kwargs.setdefault('save_mat', save_mat) # Introduce user inputs into plotting class. self.spy.lineplot([(self.general_inputs.r_nodes, self.radial_component_vacuum, r'Radial ($\hat{r}$)'), (self.general_inputs.r_nodes, self.axial_component_vacuum, r'Axial ($\hat{z}$)')], xlabel=x_label, ylabel=y_label, fig_title='Radial and axial components of the vacuum electric field', **kwargs) self.spy.lineplot([(self.general_inputs.r_nodes, self.radial_component_liquid, r'Radial ($\hat{r}$)'), (self.general_inputs.r_nodes, self.axial_component_liquid, r'Axial ($\hat{z}$)')], xlabel=x_label, ylabel=y_label, fig_title='Radial and axial components of the liquid electric field', **kwargs) self.spy.lineplot({x_label: self.general_inputs.r_mids, r'$\hat{E}_t$': E_t_array}, fig_title='Tangential component of the electric field at the meniscus', **kwargs) self.spy.lineplot([(self.general_inputs.r_mids, E_v_n_array, 'Vacuum'), (self.general_inputs.r_mids, E_l_n_array, 'Liquid')], xlabel=x_label, ylabel=y_label, fig_title='Normal components of the electric fields', **kwargs) self.spy.lineplot({x_label: self.general_inputs.r_mids, r'$\hat{\sigma}$': sigma_arr}, fig_title='Radial evolution of the surface charge density', **kwargs) self.spy.lineplot({x_label: self.general_inputs.r_mids, r'$\hat{j}_e$': j_ev_arr}, fig_title='Charge evaporation along the meniscus', **kwargs) self.spy.lineplot({x_label: self.general_inputs.r_mids, r'$\mathbf{n}\cdot\hat{\bar{\bar{\tau}}}_e \cdot \mathbf{n}$': n_taue_n_arr}, fig_title='Normal component of the electric stress at the meniscus', **kwargs)
def get_midpoints(self, boundaries, boundary_id): """ Obtain the midpoints of the facets conforming a specific boundary. Args: boundaries: Dolfin/FEniCS MeshFunction object containing the boundaries information. boundary_id: Integer identifying the specific boundary. This id comes from the .geo file. More specifically, from the id of the physical curve corresponding to the boundary. Returns: """ self.r_mids, self.z_mids = PostProcessing.get_midpoints_from_boundary(boundaries, boundary_id) return self.r_mids, self.z_mids
def get_nodepoints(self, mesh, boundaries, boundary_id): """ Obtain the node points of a given boundary. Args: mesh: Dolfin/FEniCS mesh object. boundaries: Dolfin/FEniCS MeshFunction object containing the boundaries information. boundary_id: Integer identifying the specific boundary. This id comes from the .geo file. More specifically, from the id of the physical curve corresponding to the boundary. Returns: """ self.r_nodes, self.z_nodes = PostProcessing.get_nodepoints_from_boundary(mesh, boundaries, boundary_id) return self.r_nodes, self.z_nodes
def check_evaporation_condition(u_n, j_ev, coords): """ Check the absolute difference between the normal component of the velocity and the evaporated charge. According to the problem definition, these two parameters must be equal. Args: u_n: FEniCS function of the normal component of the velocity. j_ev: FEniCS function of the evaporated charge. coords: Array containing the radial and axial coordinates of the meniscus, where this must be fulfilled. Returns: """ check = abs(u_n - j_ev) / j_ev check = PostProcessing.extract_from_function(check, coords) return check
These are useful to define functions on a specific subdomain or boundary, which is the case of the Lagrangian multiplier, which for this problem should be defined only at the interface boundary. """ # Generate the restrictions. restrictions_dict = Electrostatics.generate_restrictions() # Write out for simulation (.xml) and for visualization (.xdmf). """ As in the previous step, it is important to visually check the generated restrictions before continuing with the code. Otherwise, any error related with these restrictions will be hard to debug. """ Electrostatics.write_restrictions() # Get the midpoints and node points that define the interface boundary. r_mids, z_mids = PostProcessing.get_midpoints_from_boundary(boundaries, boundaries_ids['Interface']) coords_mids = [r_mids, z_mids] r_nodes, z_nodes = PostProcessing.get_nodepoints_from_boundary(mesh, boundaries, boundaries_ids['Interface']) coords_nodes = [r_nodes, z_nodes] # SOLVE THE ELECTROSTATICS. # Check all the options available for the implemented solver. # Poisson.check_solver_options() # Define the solver parameters. """ If the user checks the options given by the code above, one can see all the available parameters for a parameter set. These parameter sets are: - snes_solver. - krylov_solver. - lu-solver.