def __init__(self, Par, mesh): self.par = Par self.mesh = mesh # Persistent storage: Primary variables that are solved for self._psi = None self._vp = None # TODO: this will be renamed to self._a eventually # Temp storage: Stored on cells, nodes, and edges. # Used in observables and other classes and fairly general purpose self._tmp_node_var = None self._tmp_edge_var = None self._tmp_cell_var = None # Temp Storage: Allocated only for reductions self._tmp_psi_real = None self._tmp_A_real = None # copied from variables/parameters.py self.solveA = np.bool(not np.isposinf(cfg.gl_parameter)) if cfg.order_parameter == 'random': self.order_parameter = 1.0 self.randomize_order_parameter(level = cfg.random_level, seed = cfg.random_seed) else: self.order_parameter = cfg.order_parameter # set order parameter manually # vector potential is set up here instead of its setter because # we don't plan on supporting setter for it shapes = [(cfg.Nxa, cfg.Nya), (cfg.Nxb, cfg.Nyb)] self._vp = GArray(shape = shapes, dtype = cfg.dtype)
def fixed_vortices(self, fixed_vortices): self.fixed_vortices_x, self.fixed_vortices_y, self.fixed_vortices_vorticity = self._vortices_format( fixed_vortices) self._fixed_vortices_correct() if self.fixed_vortices_x.size > 0: if self._vpi is None: shapes = [(cfg.Nxa, cfg.Nya), (cfg.Nxb, cfg.Nyb)] self._vpi = GArray(shape=shapes, dtype=cfg.dtype) ai, bi = self._vpi.get_vec_h() ai.fill(0.0) bi.fill(0.0) xg, yg = self.mesh.xy_grid for k in range(self.fixed_vortices_x.size): vangle = np.arctan2(yg - self.fixed_vortices_y[k], xg - self.fixed_vortices_x[k]) vangle -= vangle[0, 0] ai += self.fixed_vortices_vorticity[k] * cfg.idx * np.diff( vangle, axis=0) bi += self.fixed_vortices_vorticity[k] * cfg.idy * np.diff( vangle, axis=1) self._vpi.need_htod_sync() self._vpi.sync() else: if self._vpi is not None: self._vpi.free() self._vpi = None self.__update_phase_lock_ns()
def _update_gvpei(self): """Sets self.gvpei = (self.ae, self.be) + (ai, bi). To be executed in self.external_vector_potential and self.fixed_vortices setters.""" assert (self.ae is None) == (self.be is None) ai, bi = None, None if self.fixed_vortices is not None and self.fixed_vortices._vpi is not None: ai, bi = self.fixed_vortices._vpi.get_vec_h() assert (ai is None) == (bi is None) vpei = None if self.ae is not None: if ai is not None: vpei = (self.ae + ai, self.be + bi) else: vpei = (self.ae, self.be) else: vpei = (ai, bi) if self._vpei is not None and vpei is None: self._vpei.free() self._vpei = None else: #TODO: easier if GArray supports like for vector storage shapes = [vpei[0].shape, vpei[1].shape] self._vpei = GArray(shape=shapes, dtype=cfg.dtype) self._vpei.set_vec_h(vpei[0], vpei[1]) self._vpei.sync()
def __init__(self, par, mesh, _vars, params, observables): self.par = par self.mesh = mesh self.vars = _vars self.params = params self.fixed_vortices = self.params.fixed_vortices self.observables = observables self.solveA = self.params.solveA self.__order_parameter_phase_lock_krnl = self.par.get_function( 'order_parameter_phase_lock') self.__iterate_order_parameter_jacobi_step_krnl = self.par.get_function( 'iterate_order_parameter_jacobi_step') self.__iterate_vector_potential_jacobi_step_krnl = self.par.get_function( 'iterate_vector_potential_jacobi_step') self.__xpy_r_krnl = self.par.get_function('xpy_r') self.__xmy_r_krnl = self.par.get_function('xmy_r') self._random_t = np.uint32(1) if cfg.random_seed is not None: self._random_t = np.uint32(cfg.random_seed) # Alloc the rhs arrays self.vars._tmp_node_var = GArray(like=self.vars._psi) shapes = [(cfg.Nxa, cfg.Nya), (cfg.Nxb, cfg.Nyb)] self.vars._tmp_edge_var = GArray(shape=shapes, dtype=cfg.dtype) A_size = self.vars.vector_potential_h().size self.__gab_next = gpuarray.zeros(A_size, dtype=cfg.dtype) self.__gpsi_next = gpuarray.empty_like(self.vars.order_parameter_h()) self.__gr2_max = gpuarray.zeros(1, dtype=np.int32) # Adjust stopping criteria according to precision if cfg.dtype is np.float32: if cfg.stop_criterion_order_parameter < 1e-6: cfg.stop_criterion_order_parameter = 1e-6 if cfg.stop_criterion_vector_potential < 1e-6: cfg.stop_criterion_vector_potential = 1e-6 else: if cfg.stop_criterion_order_parameter < 1e-12: cfg.stop_criterion_order_parameter = 1e-12 if cfg.stop_criterion_vector_potential < 1e-12: cfg.stop_criterion_vector_potential = 1e-12 cfg.stop_criterion_order_parameter = cfg.dtype( cfg.stop_criterion_order_parameter) cfg.stop_criterion_vector_potential = cfg.dtype( cfg.stop_criterion_vector_potential)
def order_parameter(self, order_parameter): if isinstance(order_parameter, (np.complexfloating, complex, np.floating, float, np.integer, int)): order_parameter = cfg.dtype_complex(order_parameter) * np.ones((cfg.Nx, cfg.Ny), cfg.dtype_complex) assert order_parameter.shape == (cfg.Nx, cfg.Ny) if self._psi is None: self._psi = GArray(like = order_parameter) else: self._psi.set_h(order_parameter) self.set_order_parameter_to_zero_outside_material() self._psi.sync()
def linear_coefficient(self, linear_coefficient): if callable(linear_coefficient): xg, yg = mesh.xy_grid lc = linear_coefficient(xg, yg) else: lc = linear_coefficient if np.isscalar(lc): lc = lc * np.ones(1) else: assert lc.shape == (cfg.Nx, cfg.Ny) self._epsilon = GArray(like=lc.astype(cfg.dtype))
def _alloc_free_temporary_gpu_storage(self, action): assert action in ['alloc', 'free'] if action == 'alloc': if self._tmp_psi_real is None: self._tmp_psi_real = GArray(self.par.grid_size, on = GArray.on_device, dtype = cfg.dtype) if self._tmp_A_real is None and self.solveA: self._tmp_A_real = GArray(self.par.grid_size_A, on = GArray.on_device, dtype = cfg.dtype) else: if self._tmp_psi_real is not None: self._tmp_psi_real.free() self._tmp_psi_real = None if self._tmp_A_real is not None: self._tmp_A_real.free() self._tmp_A_real = None
def __update_phase_lock_ns(self): if self._phase_lock_ns is not None: self._phase_lock_ns.free() self._phase_lock_ns = None if self._phase_lock_radius is not None: xg, yg = self.mesh.xy_grid lock_grid = np.full((cfg.Nx, cfg.Ny), False, dtype=np.bool) for k in range(self.fixed_vortices_x.size): lock_grid = np.logical_or( lock_grid, np.square(xg - self.fixed_vortices_x[k]) + np.square(yg - self.fixed_vortices_y[k]) <= np.square( self._phase_lock_radius)) ns = np.where(lock_grid)[0].astype(np.int32) if ns.size > 0: self._phase_lock_ns = GArray(like=ns)
def material_tiling(self, material_tiling): if callable(material_tiling): xg, yg = self.xy_c_grid mt = material_tiling(xg, yg) else: mt = material_tiling if mt is not None: assert mt.shape == (cfg.Nxc, cfg.Nyc) self._mt = GArray(like=mt.astype(np.bool)) #self.set_order_parameter_to_zero_outside_material() #self._mt.sync() else: if self._mt is not None: self._mt.free() self._mt = None
class Params(object): """This class contains setters and getters for parameters""" def __init__(self, mesh, vars): self.mesh = mesh self.vars = vars self.fixed_vortices = FixedVortices(self.mesh, self.vars) self.solveA = False self.linear_coefficient = cfg.linear_coefficient # epsilon self.gl_parameter = cfg.gl_parameter # kappa self.normal_conductivity = cfg.normal_conductivity # sigma # homogeneous external magnetic field self._H = cfg.dtype(0.0) self.homogeneous_external_field_reset = cfg.homogeneous_external_field # x- and y- components of external vector potential for non-homogeneous external magnetic field self.ae, self.be = None, None # external and irregular vector potential # it should be kept self._vpei = (self.ae, self.be) + (ai, bi) self._vpei = None # non-homogeneous external magnetic field self.external_field = cfg.external_field self.order_parameter_Langevin_coefficient = cfg.order_parameter_Langevin_coefficient self.vector_potential_Langevin_coefficient = cfg.vector_potential_Langevin_coefficient def __del__(self): pass @property def linear_coefficient(self): """ Sets/gets epsilon (linear coefficient)""" if self._epsilon.size == 1: return np.full((cfg.Nx, cfg.Ny), self._epsilon.get_h(), dtype=cfg.dtype) else: return self._epsilon.get_h() @linear_coefficient.setter def linear_coefficient(self, linear_coefficient): if callable(linear_coefficient): xg, yg = mesh.xy_grid lc = linear_coefficient(xg, yg) else: lc = linear_coefficient if np.isscalar(lc): lc = lc * np.ones(1) else: assert lc.shape == (cfg.Nx, cfg.Ny) self._epsilon = GArray(like=lc.astype(cfg.dtype)) def linear_coefficient_h(self): if self._epsilon.size != 1: return self._epsilon.get_d_obj() return np.uintp(0) def linear_coefficient_scalar_h(self): if self._epsilon.size == 1: return self._epsilon.get_h() return cfg.dtype(0.0) @property def gl_parameter(self): """ Sets/gets GL parameter""" return self._kappa @gl_parameter.setter def gl_parameter(self, gl_parameter): if gl_parameter is None or np.isnan(gl_parameter) or np.isinf( gl_parameter): gl_parameter = np.inf assert isinstance(gl_parameter, (np.floating, float, np.integer, int)) and ( np.isposinf(gl_parameter) or gl_parameter > 0.0) self._kappa = cfg.dtype(gl_parameter) self.solveA = np.bool(not np.isposinf(self._kappa)) def gl_parameter_squared_h(self): if self.solveA: return cfg.dtype(self.gl_parameter**2) return cfg.dtype(-1.0) @property def normal_conductivity(self): """ Sets/gets normal conductivity""" return self._sigma @normal_conductivity.setter def normal_conductivity(self, normal_conductivity): assert isinstance(normal_conductivity, (np.floating, float, np.integer, int)) and normal_conductivity > 0.0 self._sigma = cfg.dtype(normal_conductivity) self._rho = cfg.dtype(1.0 / normal_conductivity) @property def homogeneous_external_field(self): """ Sets/gets homogeneous external field and does not update vector potential. """ return self._H @homogeneous_external_field.setter def homogeneous_external_field(self, homogeneous_external_field): self._H = cfg.dtype(homogeneous_external_field) def _update_vector_potential(self, homogeneous_external_field, reset): assert isinstance(homogeneous_external_field, (np.floating, float, np.integer, int)) if reset: self._H = cfg.dtype(homogeneous_external_field) # TODO: need a fill method in GArray # self.a.fill(0.0) # self.b.fill(0.0) a, b = self.vars._vp.get_vec_h() a.fill(0.0) b.fill(0.0) self.vars._vp.need_htod_sync() self.vars._vp.sync() delta_H = self._H else: delta_H = -self._H self._H = cfg.dtype(homogeneous_external_field) delta_H += self._H self.vars._vp.sync() # TODO: implement GPU version of ab initialization # Possible set of gauges, A = [g*y*H, (1-g)*x*H, 0] with any g, 0 <= g <= 1 g = 0.5 _, yg = self.mesh.xy_a_grid xg, _ = self.mesh.xy_b_grid a, b = self.vars._vp.get_vec_h() a -= g * (yg - 0.5 * cfg.Ly) * delta_H b += (1.0 - g) * (xg - 0.5 * cfg.Lx) * delta_H self.vars._vp.need_htod_sync() self.vars._vp.sync() def _homogeneous_external_field_delta(self, homogeneous_external_field): self._update_vector_potential(homogeneous_external_field, reset=False) homogeneous_external_field_delta = property( fset=_homogeneous_external_field_delta, doc="""Sets homogeneous external field, H, and adds to the vector potential deltaA, satisfying curl(deltaA) = deltaH, where deltaH = H - Hold and Hold is homogeneous external field before update.""") def _homogeneous_external_field_reset(self, homogeneous_external_field): self._update_vector_potential(homogeneous_external_field, reset=True) homogeneous_external_field_reset = property( fset=_homogeneous_external_field_reset, doc="""Sets homogeneous external field, H, and sets vector potential, A, satisfying curl(A) = H.""") def _update_gvpei(self): """Sets self.gvpei = (self.ae, self.be) + (ai, bi). To be executed in self.external_vector_potential and self.fixed_vortices setters.""" assert (self.ae is None) == (self.be is None) ai, bi = None, None if self.fixed_vortices is not None and self.fixed_vortices._vpi is not None: ai, bi = self.fixed_vortices._vpi.get_vec_h() assert (ai is None) == (bi is None) vpei = None if self.ae is not None: if ai is not None: vpei = (self.ae + ai, self.be + bi) else: vpei = (self.ae, self.be) else: vpei = (ai, bi) if self._vpei is not None and vpei is None: self._vpei.free() self._vpei = None else: #TODO: easier if GArray supports like for vector storage shapes = [vpei[0].shape, vpei[1].shape] self._vpei = GArray(shape=shapes, dtype=cfg.dtype) self._vpei.set_vec_h(vpei[0], vpei[1]) self._vpei.sync() @property def external_vector_potential(self): """Sets/gets external vector potential.""" assert (self.ae is None) == (self.be is None) if self.ae is not None: return self.ae, self.be return None @external_vector_potential.setter def external_vector_potential(self, external_vector_potential): if external_vector_potential is not None: Ax, Ay = external_vector_potential assert (Ax is None) == (Ay is None) else: Ax = None if Ax is not None: assert Ax.shape == (cfg.Nxa, cfg.Nya) assert Ay.shape == (cfg.Nxb, cfg.Nyb) self.ae = Ax self.be = Ay else: self.ae, self.be = None, None self._update_gvpei() @property def external_irregular_vector_potential(self): """ Sets/gets external irregular vector potential""" if self._vpei is not None: return self._vpei.get_vec_h() return None def external_irregular_vector_potential_h(self): if self._vpei is not None: return self._vpei.get_d_obj() return np.uintp(0) @property def external_field(self): """ Sets/gets external (non-homogeneous) magnetic field. Setter accepts only a number now. """ # TODO: return curl(A) for non-homogeneous external_field A = self.external_vector_potential if A is not None: Ax, Ay = A # TODO: check expression below return (-np.diff(Ax, axis=1) * cfg.idy + np.diff(Ay, axis=0) * cfg.idx) else: return None @external_field.setter def external_field(self, external_field): if external_field is not None: # NOTE: placeholder, accepts only a number now # TODO: solve equation curl(Aext) = Hext(r) for nonuniform field Hext(r) # Possible set of gauges, A = [g*y*H, (1-g)*x*H, 0] with any g, 0 <= g <= 1 g = 0.5 _, yg = self.mesh.xy_a_grid xg, _ = self.mesh.xy_b_grid Ax = -g * (yg - 0.5 * cfg.Ly) * external_field Ay = (1.0 - g) * (xg - 0.5 * cfg.Lx) * external_field self.external_vector_potential = (Ax, Ay) else: self.external_vector_potential = None @property def order_parameter_Langevin_coefficient(self): return self._psi_langevin_c @order_parameter_Langevin_coefficient.setter def order_parameter_Langevin_coefficient( self, order_parameter_Langevin_coefficient): assert isinstance(order_parameter_Langevin_coefficient, (np.floating, float, np.integer, int)) self._psi_langevin_c = cfg.dtype(order_parameter_Langevin_coefficient) @property def vector_potential_Langevin_coefficient(self): return self._ab_langevin_c @vector_potential_Langevin_coefficient.setter def vector_potential_Langevin_coefficient( self, vector_potential_Langevin_coefficient): assert isinstance(vector_potential_Langevin_coefficient, (np.floating, float, np.integer, int)) self._ab_langevin_c = cfg.dtype(vector_potential_Langevin_coefficient)
) verbose = False rs = [0.0001, 0.001, 0.01, 0.1, 0.3, 1.0] test_e0_number, test_e0_passed = 0, 0 test_e1_number, test_e1_passed = 0, 0 test_c_number, test_c_passed = 0, 0 gl.solve._init_cg() psi0 = gl.vars.order_parameter ab0 = gl.vars.vector_potential dpsi = GArray(like = psi0) dab = GArray(shape = [ab0[0].shape, ab0[1].shape], dtype = gl.cfg.dtype) for r in rs: # The following equalities are required: # 1) a_psi + b_psi*alpha_psi = 1 # 2) a_A + b_A*alpha_A = 1, where b_A should be small a_b_alpha = [ #a_psi, b_psi, alpha_psi, a_A, b_A, alpha_A [1.0, 0.0, 0.0, 1.0-r, r, 1.0 ], # must be first (j=0) [0.5, 0.5, 1.0, 1.0-r, r, 1.0 ], [0.6976,0.72, 0.42, 1.0-r, r, 1.0 ], [0.7923,0.31, 0.67, 1.0-r, r, 1.0 ], [0.6976,0.72, 0.42, 1.0-0.7*r, 0.7*r, 1.0 ], [0.7923,0.31, 0.67, 1.0-0.6*r, 0.6*r, 1.0 ],
def _tmp_cell_var_h(self): if self._tmp_cell_var is None: self._tmp_cell_var = GArray(shape = (cfg.Nxc, cfg.Nyc), dtype = cfg.dtype) return self._tmp_cell_var.get_d_obj()
def _tmp_edge_var_h(self): if self._tmp_edge_var is None: shapes = [(cfg.Nxa, cfg.Nya), (cfg.Nxb, cfg.Nyb)] self._tmp_edge_var = GArray(shape = shapes, dtype = cfg.dtype) return self._tmp_edge_var.get_d_obj()
def _tmp_node_var_h(self): if self._tmp_node_var is None: self._tmp_node_var = GArray(like = self._psi) return self._tmp_node_var.get_d_obj()
class Vars(object): """This class contains setters and getters for solution variables order parameter and vector potential""" def __init__(self, Par, mesh): self.par = Par self.mesh = mesh # Persistent storage: Primary variables that are solved for self._psi = None self._vp = None # TODO: this will be renamed to self._a eventually # Temp storage: Stored on cells, nodes, and edges. # Used in observables and other classes and fairly general purpose self._tmp_node_var = None self._tmp_edge_var = None self._tmp_cell_var = None # Temp Storage: Allocated only for reductions self._tmp_psi_real = None self._tmp_A_real = None # copied from variables/parameters.py self.solveA = np.bool(not np.isposinf(cfg.gl_parameter)) if cfg.order_parameter == 'random': self.order_parameter = 1.0 self.randomize_order_parameter(level = cfg.random_level, seed = cfg.random_seed) else: self.order_parameter = cfg.order_parameter # set order parameter manually # vector potential is set up here instead of its setter because # we don't plan on supporting setter for it shapes = [(cfg.Nxa, cfg.Nya), (cfg.Nxb, cfg.Nyb)] self._vp = GArray(shape = shapes, dtype = cfg.dtype) def __del__(self): pass @property def order_parameter(self): self._psi.sync() psi = self._psi.get_h().copy() return psi @order_parameter.setter def order_parameter(self, order_parameter): if isinstance(order_parameter, (np.complexfloating, complex, np.floating, float, np.integer, int)): order_parameter = cfg.dtype_complex(order_parameter) * np.ones((cfg.Nx, cfg.Ny), cfg.dtype_complex) assert order_parameter.shape == (cfg.Nx, cfg.Ny) if self._psi is None: self._psi = GArray(like = order_parameter) else: self._psi.set_h(order_parameter) self.set_order_parameter_to_zero_outside_material() self._psi.sync() def order_parameter_h(self): return self._psi.get_d_obj() def set_order_parameter_to_zero_outside_material(self): if self._psi is None or not self.mesh.have_material_tiling(): return mt_at_nodes = self.mesh._get_material_tiling_at_nodes() psi = self._psi.get_h() psi[~mt_at_nodes] = 0.0 self._psi.need_htod_sync() self._psi.sync() def randomize_order_parameter(self, level=1.0, seed=None): """Randomizes order parameter: absolute value *= 1 - level*rand phase += level*pi*(2.0*rand()-1.0), where rand is uniformly distributed in [0, 1] """ assert 0.0 <= level <= 1.0 self._psi.sync() if seed is not None: np.random.seed(seed) data = (1.0 - level*np.random.rand(cfg.N)) * np.exp(level * 1.0j*np.pi*(2.0*np.random.rand(cfg.N) - 1.0)) self._psi.set_h(data) self._psi.sync() @property def vector_potential(self): if self._vp is None: return (np.zeros((cfg.Nxa, cfg.Nya), dtype=cfg.dtype), np.zeros((cfg.Nxb, cfg.Nyb), dtype=cfg.dtype)) self._vp.sync() return self._vp.get_vec_h() @vector_potential.setter def vector_potential(self, vector_potential): a, b = vector_potential self._vp.set_vec_h(a, b) self._vp.sync() def vector_potential_h(self): if self._vp is not None: return self._vp.get_d_obj() return np.uintp(0) #-------------------------- # temporary arrays #-------------------------- def _tmp_node_var_h(self): if self._tmp_node_var is None: self._tmp_node_var = GArray(like = self._psi) return self._tmp_node_var.get_d_obj() def _tmp_edge_var_h(self): if self._tmp_edge_var is None: shapes = [(cfg.Nxa, cfg.Nya), (cfg.Nxb, cfg.Nyb)] self._tmp_edge_var = GArray(shape = shapes, dtype = cfg.dtype) return self._tmp_edge_var.get_d_obj() def _tmp_cell_var_h(self): if self._tmp_cell_var is None: self._tmp_cell_var = GArray(shape = (cfg.Nxc, cfg.Nyc), dtype = cfg.dtype) return self._tmp_cell_var.get_d_obj() def _tmp_psi_real_h(self): if self._tmp_psi_real is not None: return self._tmp_psi_real.get_d_obj() return np.uintp(0) def _tmp_A_real_h(self): if self._tmp_A_real is not None: return self._tmp_A_real.get_d_obj() return np.uintp(0) def _alloc_free_temporary_gpu_storage(self, action): assert action in ['alloc', 'free'] if action == 'alloc': if self._tmp_psi_real is None: self._tmp_psi_real = GArray(self.par.grid_size, on = GArray.on_device, dtype = cfg.dtype) if self._tmp_A_real is None and self.solveA: self._tmp_A_real = GArray(self.par.grid_size_A, on = GArray.on_device, dtype = cfg.dtype) else: if self._tmp_psi_real is not None: self._tmp_psi_real.free() self._tmp_psi_real = None if self._tmp_A_real is not None: self._tmp_A_real.free() self._tmp_A_real = None
class FixedVortices(object): """This class contains methods to fix and release vortices, specify irregular vector potential""" def __init__(self, mesh, vars): # irregular vector potential self._vpi = None self._phase_lock_ns = None self._phase_lock_radius = cfg.phase_lock_radius self.mesh = mesh self.vars = vars if cfg.fixed_vortices_correction is None: cfg.fixed_vortices_correction = 'none' assert cfg.fixed_vortices_correction in ('none', 'cell centers', 'vertices') self.fixed_vortices_correction = cfg.fixed_vortices_correction self.fixed_vortices = cfg.fixed_vortices def __del__(self): pass # |psi| around isolated vortex def __vortex_psi_abs(self, x, y): r2 = x**2 + y**2 # (i) psi(0) = 0 and (ii) |psi(r)| = 1 - 1/(2*r^2) + O(1/r^4) for r to inf return (1.0 - np.exp(-r2)) / (1.0 + np.exp(-r2)) def order_parameter_add_vortices(self, vortices, phase=True, deep=False): """Adds (global) phase winding to order parameter around fixed vortex positions""" vx, vy, vv = self._vortices_format(vortices) xg, yg = self.mesh.xy_grid psi = self.vars._psi.get_h() for k in range(vx.size): if phase: psi *= np.exp(1.0j * vv[k] # add phase around vx[k], vy[k] * np.arctan2(yg - vy[k], xg - vx[k])) if deep: psi *= np.power( # add deep at vx[k], vy[k] self.__vortex_psi_abs(xg - vx[k], yg - vy[k]), np.abs(vv[k])) self.vars._psi.need_htod_sync() self.vars._psi.sync() def fixed_vortices_release(self): """Releases fixed vortices, i.e. creates natural vortices at positions (fixed_vortices_x,fixed_vortices_y) by adding phase to order parameter""" self.vars._psi.sync() psi = self.vars._psi.get_h() psi *= np.exp(-1.0j * self.fixed_vortices_phase) self.vars._psi.need_htod_sync() self.vars._psi.sync() self.fixed_vortices = None self.phase_lock_radius = None def _vortices_format(self, vortices): if vortices is None: vortices = [[], []] assert isinstance(vortices, (list, tuple, dict)) assert len(vortices) in [2, 3] if isinstance(vortices, (list, tuple)): vx, vy = vortices[0], vortices[1] vv = vortices[2] if len(vortices) == 3 else [] elif isinstance(vortices, dict): vx, vy = vortices['x'], vortices['y'] vv = vortices[2] if 'vorticity' in vortices else [] if vx is None: vx = [] if vy is None: vy = [] if vv is None: vv = [] vx, vy, vv = np.array(vx).flatten(), np.array(vy).flatten(), np.array( vv).flatten() vN = max(vx.size, vy.size, vv.size) if vx.size > 0 and vv.size == 0: vv = np.array([1]) assert vx.size in [1, vN] and vy.size in [1, vN] and vv.size in [1, vN] fvx, fvy, fvv = [], [], [] x, y, v = np.nan, np.nan, np.nan for i in range(vN): if i < vx.size: x = vx[i] if i < vy.size: y = vy[i] if i < vv.size: v = vv[i] if np.isnan(x) or np.isnan(y) or np.isnan(v): # no nans continue fvx.append(x) fvy.append(y) fvv.append(v) return (np.array(fvx).astype(cfg.dtype), np.array(fvy).astype(cfg.dtype), np.array(fvv).astype(cfg.dtype)) def _fixed_vortices_correct(self): # correction of vorticity; should be integer self.fixed_vortices_vorticity = np.round(self.fixed_vortices_vorticity) if self.fixed_vortices_correction == 'cell centers': # correction of vortex position; should be in centers of cells as magnetic field B[i,j] self.fixed_vortices_x = cfg.dx * ( np.round(self.fixed_vortices_x / cfg.dx + 0.5) - 0.5) self.fixed_vortices_y = cfg.dy * ( np.round(self.fixed_vortices_y / cfg.dy + 0.5) - 0.5) elif self.fixed_vortices_correction == 'vertices': # correction of vortex position; should be in grid vertices as order parameter psi[i,j] self.fixed_vortices_x = cfg.dx * np.round( self.fixed_vortices_x / cfg.dx) self.fixed_vortices_y = cfg.dy * np.round( self.fixed_vortices_y / cfg.dy) def _ab_phase(self, a, b): a = cfg.dx * np.r_[np.full( (1, cfg.Nya), 0.0, dtype=cfg.dtype), # reuse a and b a].cumsum(axis=0) b = cfg.dy * np.c_[np.full( (cfg.Nxb, 1), 0.0, dtype=cfg.dtype), b].cumsum(axis=1) return np.repeat(b[0:1, :], cfg.Nx, axis=0) + a # assume angle[0,0] = 0 # return b + np.repeat(a[:,0:1], cfg.Ny, axis=1) # equivalent expression @property def irregular_vector_potential(self): if self._vpi is None: return (np.zeros((cfg.Nxa, cfg.Nya), dtype=cfg.dtype), np.zeros((cfg.Nxb, cfg.Nyb), dtype=cfg.dtype)) self._vpi.sync() return self._vpi.get_vec_h() def irregular_vector_potential_h(self): if self._vpi is not None: return self._vpi.get_d_obj() return np.uintp(0) @property def fixed_vortices_phase(self): ai, bi = self.irregular_vector_potential return self._ab_phase(ai, bi) @property def fixed_vortices(self): return (self.fixed_vortices_x.copy(), self.fixed_vortices_y.copy(), self.fixed_vortices_vorticity.copy()) @fixed_vortices.setter def fixed_vortices(self, fixed_vortices): self.fixed_vortices_x, self.fixed_vortices_y, self.fixed_vortices_vorticity = self._vortices_format( fixed_vortices) self._fixed_vortices_correct() if self.fixed_vortices_x.size > 0: if self._vpi is None: shapes = [(cfg.Nxa, cfg.Nya), (cfg.Nxb, cfg.Nyb)] self._vpi = GArray(shape=shapes, dtype=cfg.dtype) ai, bi = self._vpi.get_vec_h() ai.fill(0.0) bi.fill(0.0) xg, yg = self.mesh.xy_grid for k in range(self.fixed_vortices_x.size): vangle = np.arctan2(yg - self.fixed_vortices_y[k], xg - self.fixed_vortices_x[k]) vangle -= vangle[0, 0] ai += self.fixed_vortices_vorticity[k] * cfg.idx * np.diff( vangle, axis=0) bi += self.fixed_vortices_vorticity[k] * cfg.idy * np.diff( vangle, axis=1) self._vpi.need_htod_sync() self._vpi.sync() else: if self._vpi is not None: self._vpi.free() self._vpi = None self.__update_phase_lock_ns() @property def phase_lock_radius(self): return self._phase_lock_radius def __set_phase_lock_radius(self, radius): assert radius is None or (isinstance( radius, (np.floating, float, np.integer, int)) and radius > 0.0) self._phase_lock_radius = radius #this was fixed_vortices.setter; I changed it to phase_lock_radius. @phase_lock_radius.setter def phase_lock_radius(self, radius): self.__set_phase_lock_radius(radius) self.__update_phase_lock_ns() # should update config too? #cfg.phase_lock_radius = self._phase_lock_radius def __update_phase_lock_ns(self): if self._phase_lock_ns is not None: self._phase_lock_ns.free() self._phase_lock_ns = None if self._phase_lock_radius is not None: xg, yg = self.mesh.xy_grid lock_grid = np.full((cfg.Nx, cfg.Ny), False, dtype=np.bool) for k in range(self.fixed_vortices_x.size): lock_grid = np.logical_or( lock_grid, np.square(xg - self.fixed_vortices_x[k]) + np.square(yg - self.fixed_vortices_y[k]) <= np.square( self._phase_lock_radius)) ns = np.where(lock_grid)[0].astype(np.int32) if ns.size > 0: self._phase_lock_ns = GArray(like=ns) def _phase_lock_ns_h(self): if self._phase_lock_ns is not None: return self._phase_lock_ns.get_d_obj() return np.uintp(0)
class Grid(object): """This class contains methods to retrieve the grid information at cell centroids, horizontal and vertical edge mid-points, vertices/nodes as well as material tiling. """ def __init__(self): self._mt = None self.material_tiling = cfg.material_tiling def __del__(self): pass def have_material_tiling(self): if self._mt is None: return False return True #--- grids ---# @property def xy(self): """Coordinates of grid vertices""" return (np.linspace(0.0, cfg.Lx, num=cfg.Nx, endpoint=True, dtype=cfg.dtype), np.linspace(0.0, cfg.Ly, num=cfg.Ny, endpoint=True, dtype=cfg.dtype)) @property def xy_grid(self): """Coordinates of grid vertices, Nc-by-Ny grid""" x, y = self.xy return np.meshgrid(x, y, indexing='ij') @property def xy_a(self): """Coordinates of horizontal edge centers""" return (np.linspace(0.5 * cfg.dx, cfg.Lx - 0.5 * cfg.dx, num=cfg.Nxa, endpoint=True, dtype=cfg.dtype), np.linspace(0.0, cfg.Ly, num=cfg.Nya, endpoint=True, dtype=cfg.dtype)) @property def xy_a_grid(self): """Coordinates of horizontal edge centers, Nxa-by-Nya grid""" x, y = self.xy_a return np.meshgrid(x, y, indexing='ij') @property def xy_b(self): """Coordinates of vertical edge centers""" return (np.linspace(0.0, cfg.Lx, num=cfg.Nxb, endpoint=True, dtype=cfg.dtype), np.linspace(0.5 * cfg.dy, cfg.Ly - 0.5 * cfg.dy, num=cfg.Nyb, endpoint=True, dtype=cfg.dtype)) @property def xy_b_grid(self): """Coordinates of vertical edge centers, Nxb-by-Nyb grid""" x, y = self.xy_b return np.meshgrid(x, y, indexing='ij') @property def xy_c(self): """Coordinates of cell centers""" return (np.linspace(0.5 * cfg.dx, cfg.Lx - 0.5 * cfg.dx, num=cfg.Nxc, endpoint=True, dtype=cfg.dtype), np.linspace(0.5 * cfg.dy, cfg.Ly - 0.5 * cfg.dy, num=cfg.Nyc, endpoint=True, dtype=cfg.dtype)) @property def xy_c_grid(self): """Coordinates of cell centers, Nxc-by-Nyc grid""" x, y = self.xy_c return np.meshgrid(x, y, indexing='ij') @property def material_tiling(self): """Material tiling: Returns True if material is superconducting in a cell, else return False """ if self._mt is not None: return self._mt.get_h().copy() else: return np.full((cfg.Nxc, cfg.Nyc), True, dtype=np.bool) @material_tiling.setter def material_tiling(self, material_tiling): if callable(material_tiling): xg, yg = self.xy_c_grid mt = material_tiling(xg, yg) else: mt = material_tiling if mt is not None: assert mt.shape == (cfg.Nxc, cfg.Nyc) self._mt = GArray(like=mt.astype(np.bool)) #self.set_order_parameter_to_zero_outside_material() #self._mt.sync() else: if self._mt is not None: self._mt.free() self._mt = None def material_tiling_h(self): if self._mt is not None: return self._mt.get_d_obj() return np.uintp(0) def __in_material(self, x, y, prohibited_length=None): # TODO: test in_material() method if isinstance(x, (np.floating, float, np.integer, int)) and isinstance( y, (np.floating, float, np.integer, int)): xs, ys, scalar = np.array([x], dtype=self.dtype), np.array( [y], dtype=self.dtype), True else: xs, ys, scalar = x, y, False assert xs.shape == ys.shape if prohibited_length is None: prohibited_length = self.dtype(0.0) in_mt = np.full_like(xs, True, dtype=np.bool) in_mt[np.logical_or.reduce(( xs < prohibited_length, xs > self.Lx - prohibited_length, ys < prohibited_length, ys > self.Ly - prohibited_length, ))] = False xg_c, yg_c = self.xy_c_grid xg_c, yg_c = self.flatten_c_array(xg_c), self.flatten_c_array(yg_c) in_mt = in_mt.reshape(-1) for e, (x_, y_) in enumerate(zip(xs.ravel(), ys.ravel())): if np.any( np.logical_and( np.square(xg_c - x_) + np.square(yg_c - y_) < np.square(prohibited_length), ~self.mt)): im_mt[e] = False in_mt = in_mt.reshape(xs.shape) return in_mt if not scalar else in_mt[0] def _get_material_tiling_at_nodes(self): mt = self._mt.get_h() mt_c = np.full((1, cfg.Nyc), False, dtype=np.bool) mt_r = np.full((cfg.Nx, 1), False, dtype=np.bool) return (np.logical_or.reduce( (np.c_[mt_r, np.r_[mt_c, mt]], np.c_[mt_r, np.r_[mt, mt_c]], np.c_[np.r_[mt_c, mt], mt_r], np.c_[np.r_[mt, mt_c], mt_r]))) def interpolate_ab_array_to_c_array(self, a, b): cx = 0.5 * (a[:, :-1] + a[:, 1:]) cy = 0.5 * (b[:-1, :] + b[1:, :]) return cx, cy def interpolate_ab_array_to_c_array_abs(self, a, b): cx, cy = self.interpolate_ab_array_to_c_array(a, b) return np.sqrt(np.square(cx) + np.square(cy))
gl_parameter=np.inf, homogeneous_external_field=0.1, ) verbose = False a_b_alpha = [ # a + b*alpha = 1 [1.0, 0.0, 0.0], [0.0, 1.0, 1.0], [0.5, 0.5, 1.0], ] for i in range(10): alpha, b = np.random.rand(2) a_b_alpha.append([1.0 - b * alpha, b, alpha]) dpsi = GArray(like=gl.vars.order_parameter) gl.solve._init_cg() test_p_number, test_p_passed = 0, 0 for a, b, alpha in a_b_alpha: gl.vars.order_parameter = 1.0 gl.vars.randomize_order_parameter(level=0.5) psi0 = gl.vars.order_parameter gl.params._homogeneous_external_field_reset = 0.01 + 0.1 * np.random.rand() apply_material_tiling(gl, verbose=False) E0 = gl.observables.free_energy if verbose: print('free_energy = %10.10g' % (E0))