Coverage for tests / tests_config / tests_simulation / test_config_simulation.py: 98%

106 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-09 16:40 +0100

1# SPDX-FileCopyrightText: Copyright INRIA 

2# 

3# SPDX-License-Identifier: LGPL-3.0-only 

4# 

5# Copyright INRIA 

6# 

7# This file is part of PhysioBlocks, a library mostly developed by the 

8# [Ananke project-team](https://team.inria.fr/ananke) at INRIA. 

9# 

10# Authors: 

11# - Colin Drieu 

12# - Dominique Chapelle 

13# - François Kimmig 

14# - Philippe Moireau 

15# 

16# PhysioBlocks is free software: you can redistribute it and/or modify it under the 

17# terms of the GNU Lesser General Public License as published by the Free Software 

18# Foundation, version 3 of the License. 

19# 

20# PhysioBlocks is distributed in the hope that it will be useful, but WITHOUT ANY 

21# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 

22# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 

23# 

24# You should have received a copy of the GNU Lesser General Public License along with 

25# PhysioBlocks. If not, see <https://www.gnu.org/licenses/>. 

26 

27from dataclasses import dataclass 

28from pathlib import Path 

29from unittest.mock import MagicMock, PropertyMock, patch 

30 

31import pytest 

32 

33from physioblocks.computing.models import ( 

34 BlockMetaClass, 

35 Expression, 

36 ExpressionDefinition, 

37 TermDefinition, 

38) 

39from physioblocks.computing.quantities import Quantity 

40from physioblocks.configuration.constants import NET_ID 

41from physioblocks.configuration.simulation.simulations import ( 

42 PARAMETERS_ID, 

43 load_simulation_config, 

44 save_simulation_config, 

45) 

46from physioblocks.description.blocks import ( 

47 BLOCK_DESCRIPTION_TYPE_ID, 

48 ID_SEPARATOR, 

49 Block, 

50 BlockDescription, 

51) 

52from physioblocks.description.flux import ( 

53 FLUX_TYPE_REGISTER_ID, 

54 FluxDofTypesRegister, 

55) 

56from physioblocks.description.nets import BOUNDARY_CONDITION_ID, BoundaryCondition, Net 

57from physioblocks.io.configuration import read_json 

58from physioblocks.simulation.functions import AbstractFunction 

59from physioblocks.simulation.runtime import AbstractSimulation, ForwardSimulation 

60from physioblocks.simulation.setup import SimulationFactory 

61from physioblocks.simulation.solvers import AbstractSolver 

62from physioblocks.simulation.time_manager import TIME_MANAGER_ID, TimeManager 

63from tests.helpers.assertion_helpers import ( 

64 assert_net_equals, 

65 assert_solvers_equals, 

66 assert_states_equals, 

67 assert_time_manager_equals, 

68) 

69 

70ref_config_file_path = Path( 

71 "./tests/tests_config/tests_simulation/simulation_reference.json" 

72) 

73 

74DOF_ID = "dof" 

75SCALAR_ID = "scalar" 

76VECTOR_ID = "vector" 

77NODE_A_ID = "node_a" 

78NODE_B_ID = "node_b" 

79BLOCK_A_ID = "block_a" 

80BLOCK_LOCAL_POTENTIAL = "potential_id" 

81INLET_FLUX_CONDITION_ID = "inlet_flux_condition" 

82OUTPUT_ID = "output_id" 

83FLUX_TYPE_ID = "flux_type" 

84DOF_TYPE_ID = "potential_type" 

85FUNC_TYPE_ID = "FunctionType" 

86SIM_TYPE_ID = "SimulationType" 

87BLOCK_TYPE_ID = "BlockType" 

88SOLVER_TYPE_ID = "SolverType" 

89OUTLET_POTENTIAL_CONDITION_ID = ID_SEPARATOR.join([BLOCK_A_ID, BLOCK_LOCAL_POTENTIAL]) 

90 

91 

92@dataclass 

93class BlockTest(Block): 

94 potential_id: Quantity 

95 

96 

97def ref_func(): 

98 pass 

99 

100 

101@pytest.fixture 

102def ref_term() -> TermDefinition: 

103 return TermDefinition(1, DOF_ID) 

104 

105 

106@pytest.fixture 

107def ref_expression(): 

108 return Expression(1, ref_func) 

109 

110 

111@pytest.fixture 

112def ref_expression_definition( 

113 ref_expression: Expression, ref_term: TermDefinition 

114) -> ExpressionDefinition: 

115 return ExpressionDefinition(ref_expression, [ref_term]) 

116 

117 

118@pytest.fixture 

119def ref_nodes(): 

120 return {0: [FLUX_TYPE_ID], 1: [FLUX_TYPE_ID]} 

121 

122 

123@pytest.fixture 

124def ref_flux_expressions(ref_expression_definition: ExpressionDefinition): 

125 return { 

126 0: ref_expression_definition, 

127 1: ref_expression_definition, 

128 } 

129 

130 

131@pytest.fixture 

132@patch.multiple( 

133 "physioblocks.description.nets._flux_type_register", 

134 create=True, 

135 _fluxes_types={FLUX_TYPE_ID: DOF_TYPE_ID}, 

136 _dof_types={DOF_TYPE_ID: FLUX_TYPE_ID}, 

137) 

138def net_reference(ref_nodes, ref_flux_expressions): 

139 with patch.multiple( 

140 BlockMetaClass, 

141 fluxes_expressions=PropertyMock(return_value=ref_flux_expressions), 

142 nodes=PropertyMock(return_value=ref_nodes), 

143 local_ids=PropertyMock(return_value=[BLOCK_LOCAL_POTENTIAL]), 

144 ): 

145 net = Net() 

146 net.add_node(NODE_A_ID) 

147 net.add_node(NODE_B_ID) 

148 net.add_block( 

149 BLOCK_A_ID, 

150 BlockDescription(BLOCK_A_ID, BlockTest, FLUX_TYPE_ID), 

151 {0: NODE_A_ID, 1: NODE_B_ID}, 

152 ) 

153 

154 net.set_boundary(NODE_A_ID, FLUX_TYPE_ID, INLET_FLUX_CONDITION_ID) 

155 net.set_boundary(NODE_B_ID, DOF_TYPE_ID, OUTLET_POTENTIAL_CONDITION_ID) 

156 

157 return net 

158 

159 

160def time_func(self, time): 

161 return 0.0 

162 

163 

164def func_init(self): 

165 pass 

166 

167 

168@pytest.fixture 

169@patch.multiple( 

170 "physioblocks.description.nets._flux_type_register", 

171 create=True, 

172 _fluxes_types={FLUX_TYPE_ID: DOF_TYPE_ID}, 

173 _dof_types={DOF_TYPE_ID: FLUX_TYPE_ID}, 

174) 

175def simulation_reference(net_reference: Net, ref_flux_expressions, ref_nodes): 

176 with ( 

177 patch.multiple(AbstractSimulation, __abstractmethods__=set()), 

178 patch.multiple(AbstractSolver, __abstractmethods__=set()), 

179 patch.multiple(AbstractFunction, __abstractmethods__=set(), eval=time_func), 

180 patch.multiple( 

181 BlockMetaClass, 

182 fluxes_expressions=PropertyMock(return_value=ref_flux_expressions), 

183 nodes=PropertyMock(return_value=ref_nodes), 

184 __init__=MagicMock(return_value=None), 

185 ), 

186 ): 

187 factory = SimulationFactory(AbstractSimulation, AbstractSolver(), net_reference) 

188 sim = factory.create_simulation() 

189 sim.magnitudes = {str.format("{0}.{1}", NODE_A_ID, DOF_TYPE_ID): 1.1} 

190 sim.register_timed_parameter_update(INLET_FLUX_CONDITION_ID, AbstractFunction()) 

191 sim.register_output_function(OUTPUT_ID, AbstractFunction()) 

192 sim.parameters[VECTOR_ID] = 3 * [0.0] 

193 sim.parameters[SCALAR_ID] = 0.0 

194 return sim 

195 

196 

197@patch( 

198 "physioblocks.registers.type_register.__type_register", 

199 new={ 

200 FUNC_TYPE_ID: AbstractFunction, 

201 SIM_TYPE_ID: AbstractSimulation, 

202 BLOCK_DESCRIPTION_TYPE_ID: BlockDescription, 

203 BLOCK_TYPE_ID: BlockTest, 

204 SOLVER_TYPE_ID: AbstractSolver, 

205 TIME_MANAGER_ID: TimeManager, 

206 NET_ID: Net, 

207 BOUNDARY_CONDITION_ID: BoundaryCondition, 

208 FLUX_TYPE_REGISTER_ID: FluxDofTypesRegister, 

209 AbstractFunction: FUNC_TYPE_ID, 

210 AbstractSimulation: SIM_TYPE_ID, 

211 BlockDescription: BLOCK_DESCRIPTION_TYPE_ID, 

212 BlockTest: BLOCK_TYPE_ID, 

213 AbstractSolver: SOLVER_TYPE_ID, 

214 Net: NET_ID, 

215 TimeManager: TIME_MANAGER_ID, 

216 BoundaryCondition: BOUNDARY_CONDITION_ID, 

217 FluxDofTypesRegister: FLUX_TYPE_REGISTER_ID, 

218 }, 

219) 

220@patch.multiple( 

221 "physioblocks.description.nets._flux_type_register", 

222 create=True, 

223 _fluxes_types={FLUX_TYPE_ID: DOF_TYPE_ID}, 

224 _dof_types={DOF_TYPE_ID: FLUX_TYPE_ID}, 

225) 

226def test_simulation_save_config( 

227 simulation_reference: ForwardSimulation, ref_flux_expressions, ref_nodes 

228): 

229 with ( 

230 patch.multiple( 

231 BlockMetaClass, 

232 fluxes_expressions=PropertyMock(return_value=ref_flux_expressions), 

233 nodes=PropertyMock(return_value=ref_nodes), 

234 ), 

235 patch.multiple( 

236 AbstractFunction, 

237 __abstractmethods__=set(), 

238 eval=time_func, 

239 __init__=func_init, 

240 ), 

241 ): 

242 configuration = save_simulation_config(simulation_reference) 

243 ref_config = read_json(ref_config_file_path) 

244 assert configuration == ref_config 

245 

246 

247@patch.multiple( 

248 "physioblocks.description.nets._flux_type_register", 

249 create=True, 

250 _fluxes_types={FLUX_TYPE_ID: DOF_TYPE_ID}, 

251 _dof_types={DOF_TYPE_ID: FLUX_TYPE_ID}, 

252) 

253@patch( 

254 "physioblocks.registers.type_register.__type_register", 

255 new={ 

256 FUNC_TYPE_ID: AbstractFunction, 

257 SIM_TYPE_ID: AbstractSimulation, 

258 BLOCK_DESCRIPTION_TYPE_ID: BlockDescription, 

259 BLOCK_TYPE_ID: BlockTest, 

260 SOLVER_TYPE_ID: AbstractSolver, 

261 TIME_MANAGER_ID: TimeManager, 

262 NET_ID: Net, 

263 BOUNDARY_CONDITION_ID: BoundaryCondition, 

264 AbstractFunction: FUNC_TYPE_ID, 

265 AbstractSimulation: SIM_TYPE_ID, 

266 BlockDescription: BLOCK_DESCRIPTION_TYPE_ID, 

267 BlockTest: BLOCK_TYPE_ID, 

268 AbstractSolver: SOLVER_TYPE_ID, 

269 Net: NET_ID, 

270 TimeManager: TIME_MANAGER_ID, 

271 BoundaryCondition: BOUNDARY_CONDITION_ID, 

272 }, 

273) 

274@patch.multiple(AbstractSimulation, __abstractmethods__=set()) 

275@patch.multiple(AbstractSolver, __abstractmethods__=set()) 

276@patch.multiple(AbstractFunction, __abstractmethods__=set(), eval=time_func) 

277def test_simulation_load_config( 

278 simulation_reference: ForwardSimulation, ref_flux_expressions, ref_nodes 

279): 

280 with patch.multiple( 

281 BlockMetaClass, 

282 fluxes_expressions=PropertyMock(return_value=ref_flux_expressions), 

283 nodes=PropertyMock(return_value=ref_nodes), 

284 ): 

285 ref_config = read_json(ref_config_file_path) 

286 sim = load_simulation_config(ref_config, ForwardSimulation) 

287 

288 assert sim.parameters == simulation_reference.parameters 

289 assert_net_equals(sim.factory.net, simulation_reference.factory.net) 

290 assert_solvers_equals(sim.solver, simulation_reference.solver) 

291 assert_states_equals(sim.state, simulation_reference.state) 

292 assert_time_manager_equals(sim.time_manager, simulation_reference.time_manager) 

293 

294 # load raises exceptions correctly: 

295 ref_config[PARAMETERS_ID]["wrong_type_id"] = object() 

296 with pytest.raises(TypeError): 

297 load_simulation_config(ref_config)