bindmc 0.1.3__tar.gz → 0.1.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. {bindmc-0.1.3 → bindmc-0.1.5}/PKG-INFO +3 -3
  2. {bindmc-0.1.3 → bindmc-0.1.5}/pyproject.toml +3 -3
  3. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/__main__.py +1 -1
  4. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/main.py +2 -2
  5. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/components/bayes.py +7 -2
  6. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/components/data_import.py +1 -0
  7. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/components/data_model.py +6 -2
  8. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/components/fitting.py +7 -8
  9. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/state/statemanager.py +10 -0
  10. {bindmc-0.1.3 → bindmc-0.1.5}/README.md +0 -0
  11. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/__init__.py +0 -0
  12. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/Class model.md +0 -0
  13. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/TODO.md +0 -0
  14. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/TODO_old.md +0 -0
  15. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/__init__.py +0 -0
  16. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/app.py +0 -0
  17. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/classes/BindingConstant.py +0 -0
  18. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/classes/ChemicalShiftParam.py +0 -0
  19. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/classes/Component.py +0 -0
  20. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/classes/ExptData.py +0 -0
  21. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/classes/ExptDataType.py +0 -0
  22. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/classes/FitResult.py +0 -0
  23. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/classes/MCMCSim.py +0 -0
  24. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/classes/Model.py +0 -0
  25. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/classes/RawData.py +0 -0
  26. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/classes/Simulation.py +0 -0
  27. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/classes/UIBindings.py +0 -0
  28. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/classes/__init__.py +0 -0
  29. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/components/__init__.py +0 -0
  30. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/components/base.py +0 -0
  31. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/components/bayes_priors.py +0 -0
  32. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/components/binding_model.py +0 -0
  33. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/components/body.py +0 -0
  34. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/components/data_gen.py +0 -0
  35. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/components/graph.py +0 -0
  36. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/components/header.py +0 -0
  37. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/components/simulation.py +0 -0
  38. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/default_models.json +0 -0
  39. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/export/__init__.py +0 -0
  40. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/export/notebook_exporter.py +0 -0
  41. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/state/__init__.py +0 -0
  42. {bindmc-0.1.3 → bindmc-0.1.5}/src/bindmc/webgui/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bindmc
3
- Version: 0.1.3
3
+ Version: 0.1.5
4
4
  Keywords: chemistry,analytical chemistry,binding constants,supramolecular
5
5
  Author: Martin Peeks
6
6
  Author-email: Martin Peeks <martinp23@googlemail.com>, m.peeks@unsw.edu.au
@@ -12,10 +12,10 @@ Classifier: Programming Language :: Python :: 3.14
12
12
  Classifier: Topic :: Scientific/Engineering :: Chemistry
13
13
  Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
14
14
  Requires-Dist: arviz==0.21.0
15
- Requires-Dist: bindtools>=0.1.2
15
+ Requires-Dist: bindtools>=0.1.3
16
16
  Requires-Dist: corner==2.2.3
17
17
  Requires-Dist: emcee==3.1.6
18
- Requires-Dist: h5py==3.13.0
18
+ Requires-Dist: h5py>=3.14.0
19
19
  Requires-Dist: latex2mathml>=3.0.0
20
20
  Requires-Dist: lmfit==1.3.3
21
21
  Requires-Dist: matplotlib==3.10.7
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "bindmc"
7
- version = "0.1.3"
7
+ version = "0.1.5"
8
8
  readme = "README.md"
9
9
  keywords = ["chemistry", "analytical chemistry", "binding constants", "supramolecular"]
10
10
  classifiers = [
@@ -28,10 +28,10 @@ classifiers = [
28
28
  requires-python = ">=3.12"
29
29
  dependencies = [
30
30
  "arviz==0.21.0",
31
- "bindtools>=0.1.2",
31
+ "bindtools>=0.1.3",
32
32
  "corner==2.2.3",
33
33
  "emcee==3.1.6",
34
- "h5py==3.13.0",
34
+ "h5py>=3.14.0",
35
35
  "latex2mathml>=3.0.0",
36
36
  "lmfit==1.3.3",
37
37
  "matplotlib==3.10.7",
@@ -1,4 +1,4 @@
1
1
  """Entry point for python -m bindmc."""
2
2
 
3
- if __name__ in {"__main__"}:
3
+ if __name__ in {"__main__", "__mp_main__"}:
4
4
  import bindmc.main
@@ -107,7 +107,7 @@ storage_path.mkdir(parents=True, exist_ok=True)
107
107
  # Redirect native window persistence data away from default paths
108
108
  app.native.start_args["storage_path"] = str(storage_path)
109
109
 
110
-
111
- ui.run(title="BindMC", reload=reload, native=native_mode, port=native.find_open_port(), storage_secret="bindmc_secret")
110
+ if __name__ in {"__main__", "__mp_main__"}:
111
+ ui.run(title="BindMC", reload=reload, native=native_mode, port=native.find_open_port(), storage_secret="bindmc_secret")
112
112
 
113
113
 
@@ -245,7 +245,7 @@ class BayesPanel(BaseComponent):
245
245
  return
246
246
 
247
247
  if active_fit.bd_model is None:
248
- print("No bindtools model selected for fitting, generating one.")
248
+ logger.info("No bindtools model selected for fitting, generating one.")
249
249
  ui.notify("Running an initial fit using least_sq")
250
250
  m1 = self.sm.generate_binding_model_for_fit(active_fit)
251
251
  m1 = await run.cpu_bound(
@@ -262,6 +262,7 @@ class BayesPanel(BaseComponent):
262
262
  nwalkers = int(self.nwalkers_input.value)
263
263
  obslist = self.sm.active_expt_data.get_obs_list(self.sm._expt_dtypes)
264
264
 
265
+ logger.info("Setting up for MCMC run")
265
266
  # Create MCMC simulation (not yet registered in state)
266
267
  self.mcmc = MCMCSim(
267
268
  model=self.sm.active_model,
@@ -281,12 +282,13 @@ class BayesPanel(BaseComponent):
281
282
  type="info",
282
283
  timeout=60000,
283
284
  )
285
+ logger.info("Running a trial run")
284
286
  try:
285
287
  trial_elapsed = await run.cpu_bound(partial(_run_mcmc_trial, self.mcmc.mc, _TRIAL_STEPS))
286
288
  it_s = _TRIAL_STEPS / trial_elapsed
287
289
  full_seconds = nsteps_target * trial_elapsed / _TRIAL_STEPS
288
290
  full_time_str = _format_duration(full_seconds)
289
-
291
+ logger.info("Trial run finished; took {trial_elapsed:.1f} s ({it_s:.2f} it/s).")
290
292
  with ui.dialog() as timing_dialog, ui.card().classes("w-[min(560px,92vw)]"):
291
293
  ui.label("Runtime estimate").classes("text-lg font-bold")
292
294
  ui.label(f"{_TRIAL_STEPS:,} steps took {trial_elapsed:.1f} s ({it_s:.2f} it/s).").classes(
@@ -296,6 +298,8 @@ class BayesPanel(BaseComponent):
296
298
  f"The full run ({nsteps_target:,} steps, {nwalkers} walkers) "
297
299
  f"will take approximately {full_time_str}."
298
300
  ).classes("mt-2")
301
+ logger.info(f"The full run ({nsteps_target:,} steps, {nwalkers} walkers) "
302
+ f"will take approximately {full_time_str}.")
299
303
  ui.label("Do you want to continue?").classes("mt-1 font-medium")
300
304
  with ui.row().classes("w-full justify-end gap-2 mt-3"):
301
305
  ui.button("Cancel", on_click=lambda: timing_dialog.submit(False))
@@ -320,6 +324,7 @@ class BayesPanel(BaseComponent):
320
324
  self.run_button.set_enabled(False)
321
325
  self.stop_button.set_enabled(True)
322
326
  self.progress_bar.value = 0
327
+ logger.info("Starting MCMC analysis")
323
328
  self.progress_label.text = "Starting MCMC analysis..."
324
329
  self._log_status("Starting MCMC analysis...")
325
330
  self._start_run_timers()
@@ -126,6 +126,7 @@ class DataImportPanel(BaseComponent):
126
126
  rd = active_raw
127
127
  new_expt_data = ExptData(name=rd.filename, init_raw_data=rd, init_model=self.sm.active_model)
128
128
  self.sm.add_expt_data(new_expt_data)
129
+ self.sm.notify_listeners("data_imported") # Trigger table and graph update
129
130
  else:
130
131
  ui.notify("No raw data selected to prepare data model from.", type="negative")
131
132
 
@@ -65,6 +65,10 @@ class DataModelPanel(BaseComponent):
65
65
 
66
66
  nmr_fast_ex = False
67
67
  nmr_slow_ex = False
68
+
69
+ # make all visible to allow changes in the next code block before we hide them again if not needed
70
+ self.dataModel_specInteg_block.visible = True
71
+ self.dataModel_specFastExchange_block.visible = True
68
72
 
69
73
  # work out what we need
70
74
  if self.sm.active_expt_data_or_none is not None:
@@ -76,7 +80,7 @@ class DataModelPanel(BaseComponent):
76
80
  if getattr(dtype, "meas", None) == "nmr_ppm" and f.get("depindep") == "dep":
77
81
  nmr_fast_ex = True
78
82
  self._gen_spec_fast_exchange_block()
79
- elif getattr(dtype, "meas", None) == "nmr_conc" and f.get("depindep") == "dep":
83
+ elif getattr(dtype, "meas", None) == "nmr_integ" and f.get("depindep") == "dep":
80
84
  nmr_slow_ex = True
81
85
  self._gen_spec_integ_block()
82
86
 
@@ -142,7 +146,7 @@ class DataModelPanel(BaseComponent):
142
146
  self.spec_integ_inps[spec] = ui.input().classes("flex-1").props("clearable")
143
147
 
144
148
  self.spec_integ_inps[spec].on("blur", lambda c=self.spec_integ_inps[spec]: self.set_focus(c))
145
- b = ui.checkbox("Enabled", value=True)
149
+ b = ui.checkbox("Enabled", value=True).props(f"testid=spec-enabled-{spec}")
146
150
  self.spec_integ_inps[spec].bind_enabled_from(b, "value")
147
151
  if (
148
152
  hasattr(active_expt, "integ_to_spec")
@@ -108,7 +108,7 @@ def _infer_analytical_fast_exchange_config(model, expt_data, expt_dtypes: dict)
108
108
  has_nmr = True
109
109
  elif meas in ("uvvis", "fluorescence"):
110
110
  has_linear = True
111
- else:
111
+ else:
112
112
  return None # Unknown or unsupported observable type for analytical path
113
113
 
114
114
  if has_nmr and has_linear:
@@ -442,6 +442,9 @@ class FittingPanel(BaseComponent):
442
442
  self.sm.active_expt_data,
443
443
  self.sm._expt_dtypes,
444
444
  )
445
+
446
+
447
+
445
448
  self.m1 = self.sm.generate_binding_model_for_fit(analytical_cfg=analytical_cfg)
446
449
  if analytical_cfg is not None:
447
450
  ui.notify(
@@ -492,7 +495,7 @@ class FittingPanel(BaseComponent):
492
495
  init_model=self.sm.active_model,
493
496
  bd_model=self.m1,
494
497
  analytical_fast_exchange=analytical_cfg is not None,
495
- analytical_topology=(str(analytical_cfg["topology"]) if analytical_cfg is not None else None),
498
+ analytical_topology=self.m1.analytical_topology,
496
499
  analytical_obs_columns=(
497
500
  [str(x) for x in cast(list[str], analytical_cfg["obs_columns"])]
498
501
  if analytical_cfg is not None
@@ -503,11 +506,7 @@ class FittingPanel(BaseComponent):
503
506
  if analytical_cfg is not None
504
507
  else []
505
508
  ),
506
- analytical_complex_indices=(
507
- [int(x) for x in cast(list[int], analytical_cfg["complex_indices"])]
508
- if analytical_cfg is not None
509
- else []
510
- ),
509
+ analytical_complex_indices=self.m1.analytical_complex_indices,
511
510
  )
512
511
  self.sm.add_fit(new_fit)
513
512
 
@@ -536,7 +535,7 @@ class FittingPanel(BaseComponent):
536
535
 
537
536
  def _update_fit_graphs(self, e=None) -> None:
538
537
  """Update the fit results display."""
539
- print("Updating fit results...")
538
+ logger.info("Updating fit results...")
540
539
  self.fit_graph.clear_graph(update=False)
541
540
  if len(self.sm.fits) > 0:
542
541
  self.speciation_graph.clear_graph(update=False)
@@ -1777,6 +1777,16 @@ bd.makeFitResidPlot(fit,plotMask=(0,1),ylabel='Chemical shift (ppm)')"""
1777
1777
  model.analytical_linear_obs_columns = lin_cols
1778
1778
  model.analytical_linear_obs_param_map = linear_obs_param_map
1779
1779
 
1780
+ # Always infer topology to allow analytical concentrations in slow exchange
1781
+ from bindmc.webgui.utils import _infer_simple_fast_exchange_topology
1782
+ topology_res = _infer_simple_fast_exchange_topology(
1783
+ self.active_model.eq_mat, len(self.active_model.component_names)
1784
+ )
1785
+ if topology_res is not None:
1786
+ topo_name, complex_indices = topology_res
1787
+ model.analytical_topology = topo_name
1788
+ model.analytical_complex_indices = complex_indices
1789
+
1780
1790
  model.prepModel()
1781
1791
 
1782
1792
  for k in self.active_model.binding_constants:
File without changes
File without changes
File without changes
File without changes