Skip to content

Commit 586b77e

Browse files
committed
misc: Add tests and clean up
1 parent 735e60a commit 586b77e

9 files changed

Lines changed: 230 additions & 149 deletions

File tree

devito/petsc/iet/logging.py

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ def __init__(self, level, **kwargs):
2929

3030
# TODO: To be extended with if level <= DEBUG: ...
3131

32-
# from IPython import embed; embed()
33-
# if str(self.inject_solve.expr.rhs.solver_parameters['ksp_rtol']) == '1e-15':
34-
3532
name = self.sreg.make_name(prefix='petscinfo')
3633
pname = self.sreg.make_name(prefix='petscprofiler')
3734

@@ -40,36 +37,31 @@ def __init__(self, level, **kwargs):
4037
self.section_mapper, self.inject_solve,
4138
self.function_list
4239
)
43-
# else:
44-
# name = self.sreg.make_name(prefix='petscinfooo')
45-
# pname = self.sreg.make_name(prefix='petscprofilerrrr')
46-
47-
# self.statstruct = PetscInfo(
48-
# name, pname, self.petsc_option_mapper, self.sobjs,
49-
# self.section_mapper, self.inject_solve,
50-
# self.function_list
51-
# )
52-
53-
# from IPython import embed; embed() # noqa: E402
54-
55-
# @property
56-
# def statstruct(self):
57-
# return self._statstruct
5840

5941
@cached_property
6042
def petsc_option_mapper(self):
6143
"""
62-
Create PETSc objects specifically needed for logging solver statistics.
63-
64-
ADD EXTENDED DOCSTRING
44+
For each function in `self.function_list`, look up its metadata in
45+
`petsc_return_variable_dict` and instantiate the corresponding PETSc logging
46+
variables with names from the symbol registry.
47+
48+
Example:
49+
--------
50+
>>> self.function_list
51+
['kspgetiterationnumber', 'snesgetiterationnumber', 'kspgettolerances']
52+
53+
>>> self.petsc_option_mapper
54+
{
55+
'KSPGetIterationNumber': {'kspits': kspits0},
56+
'KSPGetTolerances': {'rtol': rtol0, 'abstol': abstol0, ...}
57+
}
6558
"""
6659
opts = {}
6760
for func_name in self.function_list:
6861
info = petsc_return_variable_dict[func_name]
6962
opts[info.name] = {}
7063
for vtype, out in zip(info.variable_type, info.output_param, strict=True):
7164
opts[info.name][out] = vtype(self.sreg.make_name(prefix=out))
72-
7365
return opts
7466

7567
@cached_property
@@ -86,7 +78,7 @@ def calls(self):
8678
input = self.sobjs[return_variable.input_params]
8779
output_params = self.petsc_option_mapper[return_variable.name].values()
8880
outputs = [Byref(i) for i in output_params]
89-
# from IPython import embed; embed()
81+
9082
calls.append(
9183
petsc_call(return_variable.name, [input] + outputs)
9284
)

devito/petsc/iet/passes.py

Lines changed: 14 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import cgen as c
22
import numpy as np
33
from functools import cached_property
4-
from sympy import Not
54

65
from devito.passes.iet.engine import iet_pass
76
from devito.ir.iet import (Transformer, MapNodes, Iteration, BlankLine,
87
DummyExpr, CallableBody, List, Call, Callable,
9-
FindNodes, Section, Conditional)
8+
FindNodes, Section)
109
from devito.symbolics import Byref, FieldFromPointer, Macro
1110
from devito.types import Symbol, Scalar
1211
from devito.types.basic import DataSymbol
@@ -17,10 +16,9 @@
1716
PointerIS, Mat, CallbackVec, Vec, CallbackMat, SNES,
1817
DummyArg, PetscInt, PointerDM, PointerMat, MatReuse,
1918
CallbackPointerIS, CallbackPointerDM, JacobianStruct,
20-
SubMatrixStruct, Initialize, Finalize, ArgvSymbol,
21-
CharPtr, PetscBool)
19+
SubMatrixStruct, Initialize, Finalize, ArgvSymbol)
2220
from devito.petsc.types.macros import petsc_func_begin_user, Null
23-
from devito.petsc.iet.nodes import PetscMetaData, PETScCallable
21+
from devito.petsc.iet.nodes import PetscMetaData
2422
from devito.petsc.utils import core_metadata, petsc_languages
2523
from devito.petsc.iet.routines import (CBBuilder, CCBBuilder, BaseObjectBuilder,
2624
CoupledObjectBuilder, BaseSetup, CoupledSetup,
@@ -71,16 +69,19 @@ def lower_petsc(iet, **kwargs):
7169
# Map PETScSolve to its Section (for logging)
7270
section_mapper = MapNodes(Section, PetscMetaData, 'groupby').visit(iet)
7371

74-
# Generate a shared callback used by all PETScSolve instances to set
75-
# individual PetscOptions
76-
# set_solver_option(efuncs)
77-
# List of all callbacks that clear PetscOptions
72+
# prefixes within a single Operator should not be duplicated
73+
prefixes = [d.expr.rhs.user_prefix for d in data if d.expr.rhs.user_prefix]
74+
duplicates = {p for p in prefixes if prefixes.count(p) > 1}
7875

79-
# TODO: throw a warning/error if the user passes a solver in with the same options_prefix
80-
# it's going to lead to weird solver option behaviour. Note, if you use the options_prefix across
81-
# different Operator runs, it will not be an issue
82-
clear_options = []
76+
# TODO: How to avoid the other exception raised given it is an iet_pass?
77+
if duplicates:
78+
dup_list = ", ".join(sorted(duplicates))
79+
raise ValueError(
80+
f"The following `options_prefix` values are duplicated "
81+
f"among your PETScSolves. Ensure each one is unique: {dup_list}"
82+
)
8383

84+
clear_options = []
8485
for iters, (inject_solve,) in inject_solve_mapper.items():
8586

8687
builder = Builder(inject_solve, iters, comm, section_mapper, **kwargs)
@@ -99,7 +100,6 @@ def lower_petsc(iet, **kwargs):
99100
populate_matrix_context(efuncs)
100101

101102
iet = Transformer(subs).visit(iet)
102-
# from IPython import embed; embed()
103103
body = core + tuple(setup) + iet.body.body + tuple(clear_options)
104104
body = iet.body._rebuild(body=body)
105105
iet = iet._rebuild(body=body)
@@ -231,32 +231,6 @@ def populate_matrix_context(efuncs):
231231
)
232232

233233

234-
def set_solver_option(efuncs):
235-
236-
option = CharPtr(name='option', is_const=True)
237-
value = CharPtr(name='value', is_const=True)
238-
set = PetscBool(name='set')
239-
240-
body = List(body=[
241-
petsc_call('PetscOptionsHasName', [Null, Null, option, Byref(set)]),
242-
Conditional(Not(set), petsc_call('PetscOptionsSetValue', [Null, option, value]))
243-
])
244-
245-
body = CallableBody(
246-
body,
247-
init=(petsc_func_begin_user,),
248-
retstmt=(Call('PetscFunctionReturn', arguments=[0]),)
249-
)
250-
251-
cb = PETScCallable(
252-
'SetPetscOption',
253-
body,
254-
retval=objs['err'],
255-
parameters=(option, value)
256-
)
257-
efuncs[cb.name] = cb
258-
259-
260234
subdms = PointerDM(name='subdms')
261235
fields = PointerIS(name='fields')
262236
submats = PointerMat(name='submats')

devito/petsc/iet/routines.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,12 @@ def _make_options_callback(self):
132132
continue
133133
option_name = String(option)
134134
option_value = Null if v is None else String(str(v))
135-
set_body.append(petsc_call('PetscOptionsSetValue', [Null, option_name, option_value]))
136-
clear_body.append(petsc_call('PetscOptionsClearValue', [Null, option_name]))
135+
set_body.append(
136+
petsc_call('PetscOptionsSetValue', [Null, option_name, option_value])
137+
)
138+
clear_body.append(
139+
petsc_call('PetscOptionsClearValue', [Null, option_name])
140+
)
137141

138142
set_body = CallableBody(
139143
List(body=set_body),

devito/petsc/logging.py

Lines changed: 22 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,7 @@ def petsc_entry(self, petscinfo):
6363
containing the values for each PETSc function call.
6464
"""
6565
funcs = self._functions
66-
# from IPython import embed; embed()
6766
values = tuple(getattr(petscinfo, c) for c in funcs)
68-
# from IPython import embed; embed()
6967
return PetscEntry(**{k: v for k, v in zip(funcs, values)})
7068

7169
def _add_properties(self):
@@ -133,16 +131,14 @@ def __init__(self, name, pname, petsc_option_mapper, sobjs, section_mapper,
133131
self.section_mapper = section_mapper
134132
self.inject_solve = inject_solve
135133
self.function_list = function_list
134+
self.formatted_prefix = inject_solve.expr.rhs.formatted_prefix
136135

137136
mapper = {v: k for k, v in petsc_type_mappings.items()}
138137

139-
self.formatted_prefix = inject_solve.expr.rhs.formatted_prefix
140-
141138
fields = [
142139
(str(ptype), mapper[str(ptype._C_ctype)])
143140
for option in petsc_option_mapper.values() for ptype in option.values()
144141
]
145-
146142
super().__init__(name, pname, fields)
147143

148144
@property
@@ -152,45 +148,29 @@ def section(self):
152148

153149
@property
154150
def summary_key(self):
151+
# If users provide an options prefix, use it in the summary;
152+
# otherwise, use the default one generated by Devito
155153
user_prefix = self.inject_solve.expr.rhs.user_prefix
156-
# TODO: this will be the case when using the default options prefix provided by Devito
157-
# if user_prefix is None:
158-
# user_prefix = self.formatted_prefix
154+
if user_prefix is None:
155+
user_prefix = self.formatted_prefix
159156
return (self.section, user_prefix)
160157

161158
def __getattr__(self, attr):
162-
if attr in self.petsc_option_mapper.keys():
163-
if len(self.petsc_option_mapper[attr].values()) > 1:
164-
tmp = {}
165-
for i, j in self.petsc_option_mapper[attr].items():
166-
tmp2 = getattr(self.value._obj, j.name)
167-
tmp[i] = tmp2
168-
return tmp
169-
else:
170-
# TODO: CLEANNN
171-
first_val = list(self.petsc_option_mapper[attr].values())[0]
172-
return getattr(self.value._obj, first_val.name)
173-
# from IPython import embed; embed() # noqa: E402
174-
raise AttributeError(f"{attr} not found in PETSc return variables")
175-
176-
# TODO: maybe just overrider _hashable_content??
177-
178-
# def _hashable_content(self):
179-
# """
180-
# Return a tuple of the formatted prefix and section for hashing.
181-
# This is used to ensure that two PetscInfo objects with the same
182-
# formatted prefix and section are considered equal.
183-
# """
184-
# return (self.name, self.dtype, self.inject_solve.expr.rhs)
185-
186-
# def __eq__(self, other):
187-
# if not isinstance(other, PetscInfo):
188-
# return NotImplemented
189-
# # return self.formatted_prefix == other.formatted_prefix
190-
# return self.inject_solve.expr.rhs == other.inject_solve.expr.rhs
191-
192-
# def __hash__(self):
193-
# return hash(self.inject_solve.expr.rhs)
159+
if attr not in self.petsc_option_mapper:
160+
raise AttributeError(f"{attr} not found in PETSc return variables")
161+
162+
# Maps the petsc_option to its generated variable name e.g {'its': its0}
163+
obj_mapper = self.petsc_option_mapper[attr]
164+
165+
# Helper to get the value from the petsc profiling struct
166+
get_val = lambda v: getattr(self.value._obj, v.name)
167+
168+
# If there's only one value to retrieve for the given attribute, for example, KSPGetIterationNumber
169+
# we return it directly, otherwise we return a dictionary of all values e.g for KSPGetTolerances
170+
# we return {'rtol': val0, 'abstol': val1, ...}
171+
if len(obj_mapper) == 1:
172+
return get_val(next(iter(obj_mapper.values())))
173+
return {k: get_val(v) for k, v in obj_mapper.items()}
194174

195175

196176
# TODO: change the lists to tuples
@@ -213,13 +193,13 @@ class PetscReturnVariable:
213193
name='KSPGetIterationNumber',
214194
variable_type=[PetscInt],
215195
input_params='ksp',
216-
output_param=['kspiter']
196+
output_param=['kspits']
217197
),
218198
'snesgetiterationnumber': PetscReturnVariable(
219199
name='SNESGetIterationNumber',
220200
variable_type=[PetscInt],
221201
input_params='snes',
222-
output_param=['snesiter'],
202+
output_param=['snesits'],
223203
),
224204
'kspgettolerances': PetscReturnVariable(
225205
name='KSPGetTolerances',

devito/petsc/solve.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,9 @@ def PETScSolve(target_exprs, target=None, solver_parameters=None, options_prefix
5252
This can be passed directly to a Devito Operator.
5353
"""
5454
if target is not None:
55-
tmp = InjectSolve(
55+
return InjectSolve(
5656
solver_parameters, {target: target_exprs}, options_prefix
57-
)
58-
return tmp.build_expr()
57+
).build_expr()
5958
else:
6059
return InjectMixedSolve(
6160
solver_parameters, target_exprs, options_prefix
@@ -83,7 +82,6 @@ def build_expr(self):
8382
user_prefix=self.user_prefix,
8483
formatted_prefix=self.formatted_prefix
8584
)
86-
# from IPython import embed; embed()
8785
return PetscEq(target, linear_solve)
8886

8987
def linear_solve_args(self):

devito/petsc/solver_parameters.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def linear_solver_parameters(solver_parameters):
4747

4848
_options_prefix_counter = itertools.count()
4949

50-
# TODO: add a default options prefix if not provided
50+
5151
def format_options_prefix(options_prefix):
5252
# NOTE: Modified from the `OptionsManager` inside petsctools
5353
if options_prefix is None:

devito/petsc/types/types.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def __new__(cls, expr, solver_parameters=None,
4949
obj = sympy.Basic.__new__(cls, expr)
5050

5151
obj.expr = expr
52-
obj.solver_parameters = solver_parameters
52+
obj.solver_parameters = frozendict(solver_parameters)
5353
obj.field_data = field_data if field_data else FieldData()
5454
obj.time_mapper = time_mapper
5555
obj.localinfo = localinfo
@@ -68,12 +68,13 @@ def _sympystr(self, printer):
6868
__hash__ = sympy.Basic.__hash__
6969

7070
def _hashable_content(self):
71-
return (self.expr, self.formatted_prefix)
71+
return (self.expr, self.formatted_prefix, self.solver_parameters)
7272

7373
def __eq__(self, other):
7474
return (isinstance(other, SolveExpr) and
7575
self.expr == other.expr and
76-
self.formatted_prefix == other.formatted_prefix)
76+
self.formatted_prefix == other.formatted_prefix
77+
and self.solver_parameters == other.solver_parameters)
7778

7879
@property
7980
def grid(self):

examples/petsc/solver_options.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from devito import (Grid, Function, Eq, Operator, configuration,
55
switchconfig, Constant)
6+
from devito.tools import frozendict
67
from devito.petsc import PETScSolve
78
from devito.petsc.initialize import PetscInitialize
89
configuration['compiler'] = 'custom'
@@ -51,21 +52,29 @@
5152
eq = Eq(e.laplace, f)
5253

5354
params1 = {'ksp_view': None, 'ksp_rtol': 1e-15}
54-
params2 = {'ksp_view': None, 'ksp_rtol': 1e-11}
55+
params2 = {'ksp_rtol': 1e-12, 'ksp_view': None}
5556
petsc1 = PETScSolve(eq, target=e, solver_parameters=params1, options_prefix='pde1')
56-
petsc2 = PETScSolve(eq, target=e, solver_parameters=params2, options_prefix='pde1')
57+
petsc2 = PETScSolve(eq, target=e, solver_parameters=params2, options_prefix='pde2')
58+
59+
60+
# frozen1 = frozendict(params1)
61+
# frozen2 = frozendict(params2)
62+
63+
# assert hash(frozen1) == hash(frozen2)
5764

5865
# from IPython import embed; embed()
5966

60-
# with switchconfig(language='petsc'):
67+
with switchconfig(language='petsc', log_level='DEBUG'):
6168

62-
# op1 = Operator([petsc1, petsc2])
63-
# # op2 = Operator(petsc2)
64-
# # summary1 = op1.apply()
65-
# # summary2 = op2.apply()
69+
op1 = Operator([petsc1])
70+
# op2 = Operator(petsc2)
71+
summary1 = op1.apply()
72+
# summary2 = op2.apply()
6673

67-
# print(op1.ccode)
68-
# # print(op2.ccode)
74+
# print(op1.ccode)
75+
# print(op2.ccode)
76+
77+
petsc_summary = summary1.petsc
6978

7079

7180

0 commit comments

Comments
 (0)