def test_monitor_spherical(): pde = laplacian_spherical f = lambda th, ph: 0. g = lambda th, ph: 0. condition = DirichletBVPSpherical(r_0=0., f=f, r_1=1., g=g) monitor = MonitorSpherical(0.0, 1.0, check_every=1) solve_spherical(pde, condition, 0.0, 1.0, max_epochs=50, monitor=monitor) print("MonitorSpherical test passed")
def test_monitor_spherical(): f = lambda th, ph: 0. g = lambda th, ph: 0. conditions = [DirichletBVPSpherical(r_0=0., f=f, r_1=1., g=g)] nets = [FCNN(3, 1)] monitor = MonitorSpherical(0.0, 1.0, check_every=1) loss_history = { 'train': list(np.random.rand(10)), 'valid': list(np.random.rand(10)), } analytic_mse_history = { 'train': list(np.random.rand(10)), 'valid': list(np.random.rand(10)), } monitor.check( nets, conditions, loss_history=loss_history, analytic_mse_history=analytic_mse_history, )
def test_electric_potential_uniformly_charged_ball(): """ electric potential on uniformly charged solid sphere refer to http://www.phys.uri.edu/~gerhard/PHY204/tsl94.pdf """ # free charge volume density rho = 1. # medium permittivity epsilon = 1. # Coulomb constant k = 1. / (4 * np.pi * epsilon) # radius of the ball R = 1. # total electric charge on solid sphere Q = (4 / 3) * np.pi * (R**3) * rho # electric potential at sphere center v_0 = 1.5 * k * Q / R # electric potential on sphere boundary v_R = k * Q / R # analytic solution of electric potential analytic_solution = lambda r, th, ph: k * Q / (2 * R) * (3 - (r / R)**2) pde = lambda u, r, theta, phi: laplacian_spherical(u, r, theta, phi ) + rho / epsilon condition = DirichletBVPSpherical(r_0=0., f=lambda th, ph: v_0, r_1=R, g=lambda th, ph: v_R) monitor = MonitorSpherical(0.0, R, check_every=50) solution, loss_history, analytic_mse = solve_spherical( pde, condition, 0., R, max_epochs=500, return_best=True, analytic_solution=analytic_solution, monitor=monitor) generator = ExampleGeneratorSpherical(512) rs, thetas, phis = generator.get_examples() us = solution(rs, thetas, phis, as_type="np") vs = analytic_solution(rs, thetas, phis).detach().cpu().numpy() abs_diff = abs(us - vs) assert np.isclose(us, vs, atol=0.008).all(), \ f"Solution doesn't match analytic expectation {us} != {vs}, abs_diff={abs_diff}" print("electric-potential-on-uniformly-charged-solid-sphere passed")
def test_electric_potential_gaussian_charged_density(): # total charge Q = 1. # standard deviation of gaussian sigma = 1. # medium permittivity epsilon = 1. # Coulomb constant k = 1 / (4 * np.pi * epsilon) # coefficient of gaussian term gaussian_coeff = Q / (sigma**3) / np.power(2 * np.pi, 1.5) # distribution of charge rho_f = lambda r: gaussian_coeff * torch.exp(-r.pow(2) / (2 * sigma**2)) # analytic solution, refer to https://en.wikipedia.org/wiki/Poisson%27s_equation analytic_solution = lambda r, th, ph: (k * Q / r) * torch.erf(r / (np.sqrt( 2) * sigma)) pde = lambda u, r, th, ph: laplacian_spherical(u, r, th, ph) + rho_f( r) / epsilon r_0, r_1 = 0.1, 3. v_0 = (k * Q / r_0) * erf(r_0 / (np.sqrt(2) * sigma)) v_1 = (k * Q / r_1) * erf(r_1 / (np.sqrt(2) * sigma)) condition = DirichletBVPSpherical(r_0, lambda th, ph: v_0, r_1, lambda th, ph: v_1) monitor = MonitorSpherical(r_0, r_1, check_every=50) solution, loss_history, analytic_mse = solve_spherical( pde, condition, r_0, r_1, max_epochs=500, return_best=True, analytic_solution=analytic_solution, monitor=monitor) generator = ExampleGeneratorSpherical(512, r_min=r_0, r_max=r_1) rs, thetas, phis = generator.get_examples() us = solution(rs, thetas, phis, as_type="np") vs = analytic_solution(rs, thetas, phis).detach().cpu().numpy() rdiff = abs(us - vs) / vs assert np.isclose(us, vs, rtol=0.05).all(), \ f"Solution doesn't match analytic expectattion {us} != {vs}, relative-diff={rdiff}" print("electric-potential-on-gaussian-charged-density passed")
def test_monitor_spherical(): f = lambda th, ph: 0. g = lambda th, ph: 0. conditions = [DirichletBVPSpherical(r_0=0., f=f, r_1=1., g=g)] nets = [FCNN(3, 1)] monitor = MonitorSpherical(0.0, 1.0, check_every=1) loss_history = { 'train': list(np.random.rand(10)), 'valid': list(np.random.rand(10)), } analytic_mse_history = { 'train': list(np.random.rand(10)), 'valid': list(np.random.rand(10)), } history = { 'train_loss': list(np.random.rand(10)), 'valid_loss': list(np.random.rand(10)), 'train_foo': list(np.random.rand(10)), 'valid_foo': list(np.random.rand(10)), 'train_bar': list(np.random.rand(10)), 'valid_bar': list(np.random.rand(10)), } with pytest.warns(FutureWarning): monitor.check(nets, conditions, history=history, analytic_mse_history=analytic_mse_history) with pytest.warns(FutureWarning): monitor.check(nets, conditions, history=loss_history) with pytest.warns(FutureWarning): monitor.check(nets, conditions, history=loss_history, analytic_mse_history=analytic_mse_history) with pytest.raises(ValueError): monitor.check(nets, conditions, history={ 'train_foo': [], 'valid_foo': [] }) monitor.check(nets, conditions, history=history)
def test_electric_potential_gaussian_charged_density(): # total charge Q = 1. # standard deviation of gaussian sigma = 1. # medium permittivity epsilon = 1. # Coulomb constant k = 1 / (4 * np.pi * epsilon) # coefficient of gaussian term gaussian_coeff = Q / (sigma**3) / np.power(2 * np.pi, 1.5) # distribution of charge rho_f = lambda r: gaussian_coeff * torch.exp(-r.pow(2) / (2 * sigma**2)) # analytic solution, refer to https://en.wikipedia.org/wiki/Poisson%27s_equation analytic_solution = lambda r, th, ph: (k * Q / r) * torch.erf(r / (np.sqrt( 2) * sigma)) # interior and exterior radius r_0, r_1 = 0.1, 3. # values at interior and exterior boundary v_0 = (k * Q / r_0) * erf(r_0 / (np.sqrt(2) * sigma)) v_1 = (k * Q / r_1) * erf(r_1 / (np.sqrt(2) * sigma)) def validate(solution): generator = GeneratorSpherical(512, r_min=r_0, r_max=r_1) rs, thetas, phis = generator.get_examples() us = solution(rs, thetas, phis, to_numpy=True) vs = analytic_solution(rs, thetas, phis).detach().cpu().numpy() assert us.shape == vs.shape # solving the problem using normal network (subject to the influence of polar singularity of laplacian operator) pde1 = lambda u, r, th, ph: laplacian_spherical(u, r, th, ph) + rho_f( r) / epsilon condition1 = DirichletBVPSpherical(r_0, lambda th, ph: v_0, r_1, lambda th, ph: v_1) monitor1 = MonitorSpherical(r_0, r_1, check_every=50) with pytest.warns(FutureWarning): solution1, metrics_history = solve_spherical( pde1, condition1, r_0, r_1, max_epochs=2, return_best=True, analytic_solution=analytic_solution, monitor=monitor1, ) validate(solution1) # solving the problem using spherical harmonics (laplcian computation is optimized) max_degree = 2 harmonics_fn = RealSphericalHarmonics(max_degree=max_degree) harmonic_laplacian = HarmonicsLaplacian(max_degree=max_degree) pde2 = lambda R, r, th, ph: harmonic_laplacian(R, r, th, ph) + rho_f( r) / epsilon R_0 = torch.tensor([v_0 * 2] + [0 for _ in range((max_degree + 1)**2 - 1)]) R_1 = torch.tensor([v_1 * 2] + [0 for _ in range((max_degree + 1)**2 - 1)]) def analytic_solution2(r, th, ph): sol = torch.zeros(r.shape[0], (max_degree + 1)**2) sol[:, 0:1] = 2 * analytic_solution(r, th, ph) return sol condition2 = DirichletBVPSphericalBasis(r_0=r_0, R_0=R_0, r_1=r_1, R_1=R_1, max_degree=max_degree) monitor2 = MonitorSphericalHarmonics(r_0, r_1, check_every=50, harmonics_fn=harmonics_fn) net2 = FCNN(n_input_units=1, n_output_units=(max_degree + 1)**2) with pytest.warns(FutureWarning): solution2, metrics_history = solve_spherical( pde2, condition2, r_0, r_1, net=net2, max_epochs=2, return_best=True, analytic_solution=analytic_solution2, monitor=monitor2, harmonics_fn=harmonics_fn, ) validate(solution2)