def test_cycle_gcrotmk(self, capsys): # Load any case. dat = REGRES['res'] model = emg3d.Model(**dat['input_model']) grid = model.grid sfield = emg3d.get_source_field(**dat['input_source']) vmodel = emg3d.models.VolumeModel(model, sfield) efield = emg3d.Field(grid) # Initiate e-field. # Get var-instance var = solver.MGParameters( cycle='F', sslsolver='gcrotmk', semicoarsening=False, linerelaxation=False, shape_cells=grid.shape_cells, verb=4, maxit=5, ) var.l2_refe = sl.norm(sfield.field, check_finite=False) # Call krylov and ensure it fails properly. solver.krylov(vmodel, sfield, efield, var) out, _ = capsys.readouterr() assert 'DIVERGED' in out
def test_basic(self, capsys): # This should reach every line of solver.multigrid. dat = REGRES['res'] model = emg3d.Model(**dat['input_model']) grid = model.grid sfield = emg3d.get_source_field(**dat['input_source']) vmodel = emg3d.models.VolumeModel(model, sfield) efield = emg3d.Field(grid) # Initiate e-field. # Get var-instance var = solver.MGParameters( cycle='F', sslsolver=False, semicoarsening=True, linerelaxation=True, shape_cells=grid.shape_cells, verb=5, nu_init=2, maxit=-1, ) var.l2_refe = sl.norm(sfield.field, check_finite=False) # Call multigrid. solver.multigrid(vmodel, sfield, efield, var) out, _ = capsys.readouterr() assert '> CONVERGED' in out
def test_bicgstab_error(self, capsys): # Load any case. dat = REGRES['res'] model = emg3d.Model(**dat['input_model']) grid = model.grid model.property_x *= 100000 # Set stupid input to make bicgstab fail. model.property_y /= 100000 # Set stupid input to make bicgstab fail. sfield = emg3d.get_source_field(**dat['input_source']) vmodel = emg3d.models.VolumeModel(model, sfield) efield = emg3d.Field(grid) # Initiate e-field. # Get var-instance var = solver.MGParameters( cycle=None, sslsolver=True, semicoarsening=False, linerelaxation=False, shape_cells=grid.shape_cells, verb=3, maxit=-1, ) var.l2_refe = sl.norm(sfield.field, check_finite=False) # Call krylov and ensure it fails properly. solver.krylov(vmodel, sfield, efield, var) out, _ = capsys.readouterr() assert '* ERROR :: Error in bicgstab' in out
def test__solve(): # Has keys [model, sfield, efield, solver_opts] dat = REGRES['res'] inp = { 'model': emg3d.Model(**dat['input_model']), 'sfield': emg3d.get_source_field(**dat['input_source']), 'efield': None, 'solver_opts': { 'plain': True } } efield, info = solver._solve(inp) assert_allclose(dat['Fresult'].field, efield.field) # Has keys [model, grid, source, frequency, efield, solver_opts] dat = REGRES['res'] model = model = emg3d.Model(**dat['input_model']) inp = { 'model': model, 'grid': model.grid, 'source': dat['input_source']['source'], 'frequency': dat['input_source']['frequency'], 'efield': None, 'solver_opts': { 'plain': True } } efield, info = solver._solve(inp) assert_allclose(dat['Fresult'].field, efield.field)
def test_laplace(self, ): # Regression test for homogeneous halfspace in Laplace domain. # Not very sophisticated; replace/extend by more detailed tests. dat = REGRES['lap'] model = emg3d.Model(**dat['input_model']) grid = model.grid sfield = emg3d.get_source_field(**dat['input_source']) # F-cycle efield = solver.solve(model, sfield, plain=True) # Check all fields (ex, ey, and ez) assert_allclose(dat['Fresult'].field, efield.field, atol=1e-14) # BiCGSTAB with some print checking. efield = solver.solve(model, sfield, semicoarsening=False, linerelaxation=False) # Check all fields (ex, ey, and ez) assert_allclose(dat['bicresult'].field, efield.field, atol=1e-14) # If efield is complex, assert it fails. efield = emg3d.Field(grid, dtype=np.complex128) with pytest.raises(ValueError, match='Source field and electric fiel'): efield = solver.solve(model, sfield, plain=True, efield=efield)
def test_log(self, capsys): dat = REGRES['res'] model = emg3d.Model(**dat['input_model']) sfield = emg3d.get_source_field(**dat['input_source']) inp = {'model': model, 'sfield': sfield, 'plain': True, 'maxit': 1} efield, info = solver.solve(return_info=True, log=-1, verb=3, **inp) out, _ = capsys.readouterr() assert out == "" assert ' emg3d START ::' in info['log'] efield = solver.solve(return_info=True, log=0, verb=3, **inp) out, _ = capsys.readouterr() assert ' emg3d START ::' in out efield, info = solver.solve(return_info=True, log=1, verb=3, **inp) out, _ = capsys.readouterr() assert ' emg3d START ::' in out assert ' emg3d START ::' in info['log'] efield, info = solver.solve(return_info=True, log=1, verb=1, **inp) out, _ = capsys.readouterr() assert 'MAX. ITERATION REACHED, NOT CONVERGED' in out assert 'MAX. ITERATION REACHED, NOT CONVERGED' in info['log']
def test_amat_x(njit): if njit: amat_x = core.amat_x else: amat_x = core.amat_x.py_func # 1. Compare to alternative amat_x # Create a grid src = [200, 300, -50., 5, 60] hx = helpers.widths(8, 4, 100, 1.2) hy = np.ones(8) * 800 hz = np.ones(4) * 500 grid = emg3d.TensorMesh(h=[hx, hy, hz], origin=np.array( [-hx.sum() / 2, -hy.sum() / 2, -hz.sum() / 2])) # Create some resistivity model x = np.arange(1, grid.shape_cells[0] + 1) * 2 y = 1 / np.arange(1, grid.shape_cells[1] + 1) z = np.arange(1, grid.shape_cells[2] + 1)[::-1] / 10 property_x = np.outer(np.outer(x, y), z).ravel() freq = 0.319 model = emg3d.Model(grid=grid, property_x=property_x, property_y=0.8 * property_x, property_z=2 * property_x) # Create a source field sfield = emg3d.get_source_field(grid=grid, source=src, frequency=freq) # Get volume-averaged model parameters. vmodel = emg3d.models.VolumeModel(model, sfield) # Run two iterations to get a e-field efield = emg3d.solve(model=model, sfield=sfield, sslsolver=False, semicoarsening=False, linerelaxation=False, maxit=2, verb=1) # amat_x rr1 = emg3d.Field(grid) amat_x(rr1.fx, rr1.fy, rr1.fz, efield.fx, efield.fy, efield.fz, vmodel.eta_x, vmodel.eta_y, vmodel.eta_z, vmodel.zeta, grid.h[0], grid.h[1], grid.h[2]) # amat_x - alternative rr2 = emg3d.Field(grid) alternatives.alt_amat_x(rr2.fx, rr2.fy, rr2.fz, efield.fx, efield.fy, efield.fz, vmodel.eta_x, vmodel.eta_y, vmodel.eta_z, vmodel.zeta, grid.h[0], grid.h[1], grid.h[2]) # Check all fields (ex, ey, and ez) assert_allclose(-rr1.field, rr2.field, atol=1e-23)
def test_sc_0(self): sc = 0 # Simple test with restriction followed by prolongation. src = [150, 150, 150, 0, 45] grid = emg3d.TensorMesh( [np.ones(4) * 100, np.ones(4) * 100, np.ones(4) * 100], origin=np.zeros(3)) # Create dummy model and fields, parameters don't matter. model = emg3d.Model(grid, 1, 1, 1, 1) sfield = emg3d.get_source_field(grid, src, 1) # Get volume-averaged model parameters. vmodel = emg3d.models.VolumeModel(model, sfield) rx = np.arange(sfield.fx.size, dtype=np.complex128).reshape(sfield.fx.shape) ry = np.arange(sfield.fy.size, dtype=np.complex128).reshape(sfield.fy.shape) rz = np.arange(sfield.fz.size, dtype=np.complex128).reshape(sfield.fz.shape) field = np.r_[rx.ravel('F'), ry.ravel('F'), rz.ravel('F')] rr = emg3d.Field(grid, field) # Restrict it cmodel, csfield, cefield = solver.restriction(vmodel, sfield, rr, sc_dir=sc) assert_allclose(csfield.fx[:, 1:-1, 1], np.array([[196. + 0.j], [596. + 0.j]])) assert_allclose(csfield.fy[1:-1, :, 1], np.array([[356. + 0.j, 436. + 0.j]])) assert_allclose(csfield.fz[1:-1, 1:-1, :], np.array([[[388. + 0.j, 404. + 0.j]]])) assert cmodel.grid.shape_nodes[0] == cmodel.grid.shape_nodes[1] == 3 assert cmodel.grid.shape_nodes[2] == 3 assert cmodel.eta_x[0, 0, 0] / 8. == vmodel.eta_x[0, 0, 0] assert np.sum(grid.h[0]) == np.sum(cmodel.grid.h[0]) assert np.sum(grid.h[1]) == np.sum(cmodel.grid.h[1]) assert np.sum(grid.h[2]) == np.sum(cmodel.grid.h[2]) # Add pi to the coarse e-field efield = emg3d.Field(grid) cefield.field += np.pi # Prolong it solver.prolongation(efield, cefield, sc_dir=sc) assert np.all(efield.fx[:, 1:-1, 1:-1] == np.pi) assert np.all(efield.fy[1:-1, :, 1:-1] == np.pi) assert np.all(efield.fz[1:-1, 1:-1, :] == np.pi)
def test_residual(): # The only thing to test here is that the residual returns the same as # sfield-amat_x. Basically a copy of the function itself. # Create a grid src = [90, 1600, 25., 45, 45] grid = emg3d.TensorMesh( [helpers.widths(4, 2, 20, 1.2), np.ones(16) * 200, np.ones(2) * 25], origin=np.zeros(3)) # Create some resistivity model x = np.arange(1, grid.shape_cells[0] + 1) * 2 y = 1 / np.arange(1, grid.shape_cells[1] + 1) z = np.arange(1, grid.shape_cells[2] + 1)[::-1] / 10 property_x = np.outer(np.outer(x, y), z).ravel() freq = 0.319 model = emg3d.Model(grid, property_x, 0.8 * property_x, 2 * property_x) # Create a source field sfield = emg3d.get_source_field(grid=grid, source=src, frequency=freq) # Get volume-averaged model parameters. vmodel = emg3d.models.VolumeModel(model, sfield) # Run two iterations to get an e-field efield = solver.solve(model, sfield, maxit=2) # Use directly amat_x rfield = sfield.copy() emg3d.core.amat_x(rfield.fx, rfield.fy, rfield.fz, efield.fx, efield.fy, efield.fz, vmodel.eta_x, vmodel.eta_y, vmodel.eta_z, vmodel.zeta, grid.h[0], grid.h[1], grid.h[2]) # Compute residual out = solver.residual(vmodel, sfield, efield) outnorm = solver.residual(vmodel, sfield, efield, True) # Compare assert_allclose(out.field, rfield.field) assert_allclose(outnorm, np.linalg.norm(out.field))
def test_fields(self, capsys): # Check sfield sfield = emg3d.get_source_field(self.grid, self.survey.sources['TxEW-3'], frequency=1.0) # Check efield efield, info = emg3d.solve(self.model, sfield, **self.simulation.solver_opts) assert self.simulation.get_efield('TxEW-3', 'f-1') == efield # See a single one self.simulation._dict_efield['TxEW-3'][1.0] = None _, _ = capsys.readouterr() self.simulation.get_efield('TxEW-3', 1.0) info = self.simulation.get_efield_info('TxEW-3', 1.0) assert 'MAX. ITERATION REACHED, NOT CONVERGED' in info['exit_message'] # Check hfield hfield = emg3d.get_magnetic_field(self.model, efield) assert self.simulation.get_hfield('TxEW-3', 1.0) == hfield s_hfield = self.simulation.get_hfield('TxEW-3', 1.0) assert s_hfield == hfield assert_allclose( self.simulation._dict_efield_info['TxEW-3']['f-1']['abs_error'], info['abs_error']) assert_allclose( self.simulation._dict_efield_info['TxEW-3']['f-1']['rel_error'], info['rel_error']) exit = self.simulation._dict_efield_info['TxEW-3']['f-1']['exit'] assert exit == info['exit'] == 1 # First hfield, ensure efield/hfield get computed. sim = self.simulation.copy(what='all') sim._dict_efield['TxEW-3']['f-1'] = None sim.get_hfield('TxEW-3', 'f-1') assert sim._dict_efield['TxEW-3']['f-1'] is not None
def test_gauss_seidel(njit): if njit: gauss_seidel = core.gauss_seidel gauss_seidel_x = core.gauss_seidel_x gauss_seidel_y = core.gauss_seidel_y gauss_seidel_z = core.gauss_seidel_z else: gauss_seidel = core.gauss_seidel.py_func gauss_seidel_x = core.gauss_seidel_x.py_func gauss_seidel_y = core.gauss_seidel_y.py_func gauss_seidel_z = core.gauss_seidel_z.py_func # At the moment we only compare `gauss_seidel_x/y/z` to `gauss_seidel`. # Better tests would always be welcomed... # Rotate the source, so we have a strong enough signal in all directions src = [0, 0, 0, 45, 45] freq = 0.9 nu = 2 # One back-and-forth for lr_dir in range(1, 4): # `gauss_seidel`/`_x/y/z` loop over z, then y, then x. Together with # `lr_dir`, we have to keep the dimension at 2 in order that they # agree. nx = [1, 4, 4][lr_dir - 1] ny = [4, 1, 4][lr_dir - 1] nz = [4, 4, 1][lr_dir - 1] # Get this grid. hx = helpers.widths(0, nx, 80, 1.1) hy = helpers.widths(0, ny, 100, 1.3) hz = helpers.widths(0, nz, 200, 1.2) grid = emg3d.TensorMesh( [hx, hy, hz], np.array([-hx.sum() / 2, -hy.sum() / 2, -hz.sum() / 2])) # Initialize model with some resistivities. property_x = np.arange(grid.n_cells) + 1 property_y = 0.5 * np.arange(grid.n_cells) + 1 property_z = 2 * np.arange(grid.n_cells) + 1 model = emg3d.Model(grid, property_x, property_y, property_z) # Initialize source field. sfield = emg3d.get_source_field(grid, src, freq) # Get volume-averaged model parameters. vmodel = emg3d.models.VolumeModel(model, sfield) # Run two iterations to get some e-field. efield = emg3d.solve(model, sfield, sslsolver=False, semicoarsening=False, linerelaxation=False, maxit=2, verb=1) inp = (sfield.fx, sfield.fy, sfield.fz, vmodel.eta_x, vmodel.eta_y, vmodel.eta_z, vmodel.zeta, grid.h[0], grid.h[1], grid.h[2], nu) # Get result from `gauss_seidel`. cfield = emg3d.Field(grid, efield.field.copy(), frequency=efield._frequency) gauss_seidel(cfield.fx, cfield.fy, cfield.fz, *inp) # Get result from `gauss_seidel_x/y/z`. if lr_dir == 1: gauss_seidel_x(efield.fx, efield.fy, efield.fz, *inp) elif lr_dir == 2: gauss_seidel_y(efield.fx, efield.fy, efield.fz, *inp) elif lr_dir == 3: gauss_seidel_z(efield.fx, efield.fy, efield.fz, *inp) # Check the resulting field. assert efield == cfield
def test_smoothing(): # 1. The only thing to test here is that smoothing returns the same as # the corresponding jitted functions. Basically a copy of the function # itself. nu = 2 widths = [ np.ones(2) * 100, helpers.widths(10, 27, 10, 1.1), helpers.widths(2, 1, 50, 1.2) ] origin = [-w.sum() / 2 for w in widths] src = [0, -10, -10, 43, 13] # Loop and move the 2-cell dimension (100, 2) from x to y to z. for xyz in range(3): # Create a grid grid = emg3d.TensorMesh( [widths[xyz % 3], widths[(xyz + 1) % 3], widths[(xyz + 2) % 3]], origin=np.array([ origin[xyz % 3], origin[(xyz + 1) % 3], origin[(xyz + 2) % 3] ])) # Create some resistivity model x = np.arange(1, grid.shape_cells[0] + 1) * 2 y = 1 / np.arange(1, grid.shape_cells[1] + 1) z = np.arange(1, grid.shape_cells[2] + 1)[::-1] / 10 property_x = np.outer(np.outer(x, y), z).ravel() freq = 0.319 model = emg3d.Model(grid, property_x, 0.8 * property_x, 2 * property_x) # Create a source field sfield = emg3d.get_source_field(grid=grid, source=src, frequency=freq) # Get volume-averaged model parameters. vmodel = emg3d.models.VolumeModel(model, sfield) # Run two iterations to get an e-field field = solver.solve(model, sfield, maxit=2) # Collect Gauss-Seidel input (same for all routines) inp = (sfield.fx, sfield.fy, sfield.fz, vmodel.eta_x, vmodel.eta_y, vmodel.eta_z, vmodel.zeta, grid.h[0], grid.h[1], grid.h[2], nu) func = ['', '_x', '_y', '_z'] for lr_dir in range(8): # Get it directly from core efield = emg3d.Field(grid, field.field) finp = (efield.fx, efield.fy, efield.fz) if lr_dir < 4: getattr(emg3d.core, 'gauss_seidel' + func[lr_dir])(efield.fx, efield.fy, efield.fz, *inp) elif lr_dir == 4: emg3d.core.gauss_seidel_y(*finp, *inp) emg3d.core.gauss_seidel_z(*finp, *inp) elif lr_dir == 5: emg3d.core.gauss_seidel_x(*finp, *inp) emg3d.core.gauss_seidel_z(*finp, *inp) elif lr_dir == 6: emg3d.core.gauss_seidel_x(*finp, *inp) emg3d.core.gauss_seidel_y(*finp, *inp) elif lr_dir == 7: emg3d.core.gauss_seidel_x(*finp, *inp) emg3d.core.gauss_seidel_y(*finp, *inp) emg3d.core.gauss_seidel_z(*finp, *inp) # Use solver.smoothing ofield = emg3d.Field(grid, field.field) solver.smoothing(vmodel, sfield, ofield, nu, lr_dir) # Compare assert efield == ofield
def test_homogeneous(self, capsys): # Regression test for homogeneous halfspace. dat = REGRES['res'] model = emg3d.Model(**dat['input_model']) grid = model.grid sfield = emg3d.get_source_field(**dat['input_source']) # F-cycle efield = solver.solve(model=model, sfield=sfield, plain=True, verb=4) out, _ = capsys.readouterr() assert ' emg3d START ::' in out assert ' [hh:mm:ss] ' in out assert ' MG cycles ' in out assert ' Final rel. error ' in out assert ' emg3d END :: ' in out # Experimental: # Check if norms are also the same, at least for first two cycles. assert "3.399e-02 after 1 F-cycles [1.830e-07, 0.034] 0 " in out assert "3.535e-03 after 2 F-cycles [1.903e-08, 0.104] 0 " in out # Check all fields (ex, ey, and ez) assert_allclose(dat['Fresult'].field, efield.field) # W-cycle wfield = solver.solve(model, sfield, plain=True, cycle='W') # Check all fields (ex, ey, and ez) assert_allclose(dat['Wresult'].field, wfield.field) # V-cycle vfield = solver.solve(model, sfield, plain=True, cycle='V') _, _ = capsys.readouterr() # clear output # Check all fields (ex, ey, and ez) assert_allclose(dat['Vresult'].field, vfield.field) # BiCGSTAB with some print checking. efield = solver.solve(model, sfield, verb=4, sslsolver='bicgstab', plain=True) out, _ = capsys.readouterr() assert ' emg3d START ::' in out assert ' [hh:mm:ss] ' in out assert ' CONVERGED' in out assert ' Solver steps ' in out assert ' MG prec. steps ' in out assert ' Final rel. error ' in out assert ' emg3d END :: ' in out # Check all fields (ex, ey, and ez) assert_allclose(dat['bicresult'].field, efield.field) # Same as previous, without BiCGSTAB, but some print checking. efield = solver.solve(model, sfield, plain=True, verb=4) out, _ = capsys.readouterr() assert ' emg3d START ::' in out assert ' [hh:mm:ss] ' in out assert ' CONVERGED' in out assert ' MG cycles ' in out assert ' Final rel. error ' in out assert ' emg3d END :: ' in out # Max it maxit = 2 _, info = solver.solve(model, sfield, plain=True, verb=3, maxit=maxit, return_info=True) out, _ = capsys.readouterr() assert ' MAX. ITERATION REACHED' in out assert maxit == info['it_mg'] assert info['exit'] == 1 assert 'MAX. ITERATION REACHED' in info['exit_message'] # BiCGSTAB with lower verbosity, print checking. _ = solver.solve(model, sfield, sslsolver='bicgstab', plain=True, verb=3, maxit=1) out, _ = capsys.readouterr() assert ' MAX. ITERATION REACHED' in out # Just check if it runs without failing for other solvers. _ = solver.solve(model, sfield, sslsolver='gcrotmk', plain=True, verb=5, maxit=1) # Provide initial field. _, _ = capsys.readouterr() # empty efield_copy = efield.copy() outarray = solver.solve(model, sfield, plain=True, efield=efield_copy, verb=3) out, _ = capsys.readouterr() # Ensure there is no output. assert outarray is None assert "NOTHING DONE (provided efield already good enough)" in out # Ensure the field did not change. assert_allclose(efield.field, efield_copy.field) # Provide initial field and return info. info = solver.solve(model, sfield, plain=True, efield=efield_copy, return_info=True) assert info['it_mg'] == 0 assert info['it_ssl'] == 0 assert info['exit'] == 0 assert info['exit_message'] == 'CONVERGED' # Provide initial field, ensure one initial multigrid is carried out # without linerelaxation nor semicoarsening. _, _ = capsys.readouterr() # empty efield = emg3d.Field(grid) outarray = solver.solve(model, sfield, efield=efield, maxit=2, verb=4) out, _ = capsys.readouterr() assert "after 1 F-cycles 4 1" in out assert "after 2 F-cycles 5 2" in out # Provide an initial source-field without frequency information. wrong_sfield = emg3d.Field(grid) wrong_sfield.field = sfield.field with pytest.raises(ValueError, match="Source field is missing frequ"): solver.solve(model, wrong_sfield, plain=True, efield=efield, verb=1) # Check stagnation by providing an almost zero source field. sfield.field = 1e-10 _ = solver.solve(model, sfield, plain=True, maxit=100) out, _ = capsys.readouterr() assert "STAGNATED" in out # Check a zero field is returned for a zero source field. sfield.field = 0 efield = solver.solve(model, sfield, plain=True, maxit=100, verb=3) out, _ = capsys.readouterr() assert "RETURN ZERO E-FIELD (provided sfield is zero)" in out assert np.linalg.norm(efield.field) == 0.0
def test_print_one_liner(capsys): var = solver.MGParameters(verb=5, cycle='F', sslsolver=False, linerelaxation=False, semicoarsening=False, shape_cells=(16, 8, 2)) solver._print_one_liner(var, 1e-2, False) out, _ = capsys.readouterr() assert ":: emg3d :: 1.0e-02; 0; 0:00:0" in out var = solver.MGParameters(verb=5, cycle='F', sslsolver=True, linerelaxation=False, semicoarsening=False, shape_cells=(16, 8, 2)) var.exit_message = "TEST" solver._print_one_liner(var, 1e-2, True) out, _ = capsys.readouterr() assert ":: emg3d :: 1.0e-02; 0(0); 0:00:0" in out assert "TEST" in out grid = emg3d.TensorMesh( [np.ones(8), np.ones(8), np.ones(8)], origin=np.array([0, 0, 0])) model = emg3d.Model(grid, property_x=1.5, property_y=1.8, property_z=3.3) sfield = emg3d.get_source_field(grid, source=[4, 4, 4, 0, 0], frequency=10.0) # Dynamic one-liner. out, _ = capsys.readouterr() _ = solver.solve(model, sfield, sslsolver=False, semicoarsening=False, linerelaxation=False, verb=1) out, _ = capsys.readouterr() assert '6; 0:00:' in out assert '; CONVERGED' in out out, _ = capsys.readouterr() _ = solver.solve(model, sfield, sslsolver=True, semicoarsening=False, linerelaxation=False, verb=1) out, _ = capsys.readouterr() assert '3(5); 0:00:' in out assert '; CONVERGED' in out # One-liner. out, _ = capsys.readouterr() _ = solver.solve(model, sfield, sslsolver=True, semicoarsening=False, linerelaxation=False, verb=1) out, _ = capsys.readouterr() assert '3(5); 0:00:' in out assert '; CONVERGED' in out