pychnosz 1.1.1__cp310-cp310-win_amd64.whl

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 (128) hide show
  1. pychnosz/__init__.py +129 -0
  2. pychnosz/biomolecules/__init__.py +29 -0
  3. pychnosz/biomolecules/ionize_aa.py +197 -0
  4. pychnosz/biomolecules/proteins.py +595 -0
  5. pychnosz/core/__init__.py +46 -0
  6. pychnosz/core/affinity.py +1256 -0
  7. pychnosz/core/animation.py +593 -0
  8. pychnosz/core/balance.py +334 -0
  9. pychnosz/core/basis.py +716 -0
  10. pychnosz/core/diagram.py +3336 -0
  11. pychnosz/core/equilibrate.py +813 -0
  12. pychnosz/core/equilibrium.py +554 -0
  13. pychnosz/core/info.py +821 -0
  14. pychnosz/core/retrieve.py +364 -0
  15. pychnosz/core/speciation.py +580 -0
  16. pychnosz/core/species.py +599 -0
  17. pychnosz/core/subcrt.py +1700 -0
  18. pychnosz/core/thermo.py +593 -0
  19. pychnosz/core/unicurve.py +1226 -0
  20. pychnosz/data/__init__.py +11 -0
  21. pychnosz/data/add_obigt.py +327 -0
  22. pychnosz/data/extdata/Berman/BDat17_2017.csv +2 -0
  23. pychnosz/data/extdata/Berman/Ber88_1988.csv +68 -0
  24. pychnosz/data/extdata/Berman/Ber90_1990.csv +5 -0
  25. pychnosz/data/extdata/Berman/DS10_2010.csv +6 -0
  26. pychnosz/data/extdata/Berman/FDM+14_2014.csv +2 -0
  27. pychnosz/data/extdata/Berman/Got04_2004.csv +5 -0
  28. pychnosz/data/extdata/Berman/JUN92_1992.csv +3 -0
  29. pychnosz/data/extdata/Berman/SHD91_1991.csv +12 -0
  30. pychnosz/data/extdata/Berman/VGT92_1992.csv +2 -0
  31. pychnosz/data/extdata/Berman/VPT01_2001.csv +3 -0
  32. pychnosz/data/extdata/Berman/VPV05_2005.csv +2 -0
  33. pychnosz/data/extdata/Berman/ZS92_1992.csv +11 -0
  34. pychnosz/data/extdata/Berman/sympy.R +99 -0
  35. pychnosz/data/extdata/Berman/testing/BA96.bib +12 -0
  36. pychnosz/data/extdata/Berman/testing/BA96_Berman.csv +21 -0
  37. pychnosz/data/extdata/Berman/testing/BA96_OBIGT.csv +21 -0
  38. pychnosz/data/extdata/Berman/testing/BA96_refs.csv +6 -0
  39. pychnosz/data/extdata/OBIGT/AD.csv +25 -0
  40. pychnosz/data/extdata/OBIGT/Berman_cr.csv +93 -0
  41. pychnosz/data/extdata/OBIGT/DEW.csv +211 -0
  42. pychnosz/data/extdata/OBIGT/H2O_aq.csv +4 -0
  43. pychnosz/data/extdata/OBIGT/SLOP98.csv +411 -0
  44. pychnosz/data/extdata/OBIGT/SUPCRT92.csv +178 -0
  45. pychnosz/data/extdata/OBIGT/inorganic_aq.csv +729 -0
  46. pychnosz/data/extdata/OBIGT/inorganic_cr.csv +273 -0
  47. pychnosz/data/extdata/OBIGT/inorganic_gas.csv +20 -0
  48. pychnosz/data/extdata/OBIGT/organic_aq.csv +1104 -0
  49. pychnosz/data/extdata/OBIGT/organic_cr.csv +481 -0
  50. pychnosz/data/extdata/OBIGT/organic_gas.csv +268 -0
  51. pychnosz/data/extdata/OBIGT/organic_liq.csv +533 -0
  52. pychnosz/data/extdata/OBIGT/testing/GEMSFIT.csv +43 -0
  53. pychnosz/data/extdata/OBIGT/testing/IGEM.csv +17 -0
  54. pychnosz/data/extdata/OBIGT/testing/Sandia.csv +8 -0
  55. pychnosz/data/extdata/OBIGT/testing/SiO2.csv +4 -0
  56. pychnosz/data/extdata/misc/AD03_Fig1a.csv +69 -0
  57. pychnosz/data/extdata/misc/AD03_Fig1b.csv +43 -0
  58. pychnosz/data/extdata/misc/AD03_Fig1c.csv +89 -0
  59. pychnosz/data/extdata/misc/AD03_Fig1d.csv +30 -0
  60. pychnosz/data/extdata/misc/BZA10.csv +5 -0
  61. pychnosz/data/extdata/misc/HW97_Cp.csv +90 -0
  62. pychnosz/data/extdata/misc/HWM96_V.csv +229 -0
  63. pychnosz/data/extdata/misc/LA19_test.csv +7 -0
  64. pychnosz/data/extdata/misc/Mer75_Table4.csv +42 -0
  65. pychnosz/data/extdata/misc/OBIGT_check.csv +423 -0
  66. pychnosz/data/extdata/misc/PM90.csv +7 -0
  67. pychnosz/data/extdata/misc/RH95.csv +23 -0
  68. pychnosz/data/extdata/misc/RH98_Table15.csv +17 -0
  69. pychnosz/data/extdata/misc/SC10_Rainbow.csv +19 -0
  70. pychnosz/data/extdata/misc/SK95.csv +55 -0
  71. pychnosz/data/extdata/misc/SOJSH.csv +61 -0
  72. pychnosz/data/extdata/misc/SS98_Fig5a.csv +81 -0
  73. pychnosz/data/extdata/misc/SS98_Fig5b.csv +84 -0
  74. pychnosz/data/extdata/misc/TKSS14_Fig2.csv +25 -0
  75. pychnosz/data/extdata/misc/bluered.txt +1000 -0
  76. pychnosz/data/extdata/protein/Cas/Cas_aa.csv +177 -0
  77. pychnosz/data/extdata/protein/Cas/Cas_uniprot.csv +186 -0
  78. pychnosz/data/extdata/protein/Cas/download.R +34 -0
  79. pychnosz/data/extdata/protein/Cas/mkaa.R +34 -0
  80. pychnosz/data/extdata/protein/POLG.csv +12 -0
  81. pychnosz/data/extdata/protein/TBD+05.csv +393 -0
  82. pychnosz/data/extdata/protein/TBD+05_aa.csv +393 -0
  83. pychnosz/data/extdata/protein/rubisco.csv +28 -0
  84. pychnosz/data/extdata/protein/rubisco.fasta +239 -0
  85. pychnosz/data/extdata/protein/rubisco_aa.csv +28 -0
  86. pychnosz/data/extdata/src/H2O92D.f.orig +3457 -0
  87. pychnosz/data/extdata/src/README.txt +5 -0
  88. pychnosz/data/extdata/taxonomy/names.dmp +215 -0
  89. pychnosz/data/extdata/taxonomy/nodes.dmp +63 -0
  90. pychnosz/data/extdata/thermo/Bdot_acirc.csv +60 -0
  91. pychnosz/data/extdata/thermo/buffer.csv +40 -0
  92. pychnosz/data/extdata/thermo/element.csv +135 -0
  93. pychnosz/data/extdata/thermo/groups.csv +6 -0
  94. pychnosz/data/extdata/thermo/opt.csv +2 -0
  95. pychnosz/data/extdata/thermo/protein.csv +506 -0
  96. pychnosz/data/extdata/thermo/refs.csv +343 -0
  97. pychnosz/data/extdata/thermo/stoich.csv.xz +0 -0
  98. pychnosz/data/loader.py +431 -0
  99. pychnosz/data/mod_obigt.py +322 -0
  100. pychnosz/data/obigt.py +471 -0
  101. pychnosz/data/worm.py +228 -0
  102. pychnosz/fortran/__init__.py +16 -0
  103. pychnosz/fortran/h2o92.dll +0 -0
  104. pychnosz/fortran/h2o92_interface.py +527 -0
  105. pychnosz/geochemistry/__init__.py +21 -0
  106. pychnosz/geochemistry/minerals.py +514 -0
  107. pychnosz/geochemistry/redox.py +500 -0
  108. pychnosz/models/__init__.py +47 -0
  109. pychnosz/models/archer_wang.py +165 -0
  110. pychnosz/models/berman.py +309 -0
  111. pychnosz/models/cgl.py +381 -0
  112. pychnosz/models/dew.py +997 -0
  113. pychnosz/models/hkf.py +523 -0
  114. pychnosz/models/hkf_helpers.py +222 -0
  115. pychnosz/models/iapws95.py +1113 -0
  116. pychnosz/models/supcrt92_fortran.py +238 -0
  117. pychnosz/models/water.py +480 -0
  118. pychnosz/utils/__init__.py +27 -0
  119. pychnosz/utils/expression.py +1074 -0
  120. pychnosz/utils/formula.py +830 -0
  121. pychnosz/utils/formula_ox.py +227 -0
  122. pychnosz/utils/reset.py +33 -0
  123. pychnosz/utils/units.py +259 -0
  124. pychnosz-1.1.1.dist-info/METADATA +197 -0
  125. pychnosz-1.1.1.dist-info/RECORD +128 -0
  126. pychnosz-1.1.1.dist-info/WHEEL +5 -0
  127. pychnosz-1.1.1.dist-info/licenses/LICENSE.txt +19 -0
  128. pychnosz-1.1.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,593 @@
1
+ import plotly.express as px
2
+ import plotly.graph_objects as go
3
+ import copy
4
+ import numpy as np
5
+ import pandas as pd
6
+ import statistics
7
+
8
+ # Import chnosz functions
9
+ from .basis import basis
10
+ from .species import species
11
+ from .affinity import affinity
12
+ from .equilibrate import equilibrate
13
+ from .diagram import diagram, diagram_interactive
14
+ from .info import info
15
+
16
+ # Import chemlabel for formatting chemical formulas
17
+ try:
18
+ from WORMutils import chemlabel
19
+ except ImportError:
20
+ # Fallback: simple identity function if WORMutils is not available
21
+ def chemlabel(s):
22
+ """Simple fallback for chemical label formatting."""
23
+ return str(s)
24
+
25
+
26
+ def __seq(start, end, by=None, length_out=None):
27
+
28
+ """
29
+ Mimic the seq() function in base R.
30
+ """
31
+
32
+ len_provided = True if (length_out is not None) else False
33
+ by_provided = True if (by is not None) else False
34
+ if (not by_provided) & (not len_provided):
35
+ raise ValueError('At least by or n_points must be provided')
36
+ width = end - start
37
+ eps = pow(10.0, -14)
38
+ if by_provided:
39
+ if (abs(by) < eps):
40
+ raise ValueError('by must be non-zero.')
41
+ absby = abs(by)
42
+ if absby - width < eps:
43
+ length_out = int(width / absby)
44
+ else:
45
+ # by is too great, we assume by is actually length_out
46
+ length_out = int(by)
47
+ by = width / (by - 1)
48
+ else:
49
+ length_out = int(length_out)
50
+ by = width / (length_out - 1)
51
+ out = [float(start)]*length_out
52
+ for i in range(1, length_out):
53
+ out[i] += by * i
54
+ if abs(start + by * length_out - end) < eps:
55
+ out.append(end)
56
+ return out
57
+
58
+
59
+ def animation(basis_args={}, species_args={}, affinity_args={},
60
+ equilibrate_args=None, diagram_args={},
61
+ anim_var="T", anim_range=[0, 350, 8], xlab=None, ylab=None,
62
+ save_as="newanimationframe", save_format="png", height=300,
63
+ width=400, save_scale=1,
64
+ messages=False):
65
+
66
+ """
67
+ Produce an animated interactive affinity, activity, or predominance diagram.
68
+
69
+ Parameters
70
+ ----------
71
+ basis_args : dict
72
+ Dictionary of options for defining basis species (see `basis`) in the
73
+ animated diagram.
74
+ Example: basis_args={'species':['CO2', 'O2', 'H2O', 'H+']}
75
+
76
+ species_args : dict
77
+ Dictionary of options for defining species (see `species`) in the
78
+ animated diagram, or a list of dicts.
79
+ Example 1: species_args={'species':['CO2', 'HCO3-', 'CO3-2']}
80
+ Example 2: species_args=[
81
+ {'species':['CO2', 'HCO3-', 'CO3-2'], 'state':[-4]},
82
+ {'species':['graphite'], state:[0], 'add':True}]
83
+
84
+ affinity_args : dict
85
+ Dictionary of options for defining the affinity calculation (see
86
+ `affinity`).
87
+ Example: affinity_args={"pH":[2, 12, 100]}
88
+ Example: affinity_args={"pH":[2, 12, 100], "P":[2000, 4000, 100]}
89
+
90
+ equilibrate_args : dict or None, default None
91
+ Dictionary of options for defining equilibration calculation
92
+ (see `equilibrate`). If None, plots output from `affinity`.
93
+ Example: equilibrate_args={"balance":1}
94
+
95
+ diagram_args : dict
96
+ Dictionary of options for diagramming (see `diagram`). Diagram option
97
+ `interactive` is set to True.
98
+ Example: diagram_args={"alpha":True}
99
+
100
+ anim_var : str, default "T"
101
+ Variable that changes with each frame of animation.
102
+
103
+ anim_range : list of numeric, default [0, 350, 8]
104
+ The first two numbers in the list are the starting and ending
105
+ values for `anim_var`. The third number in the list is the desired
106
+ number of animation frames.
107
+
108
+ xlab, ylab : str, optional
109
+ Custom names for the X and Y axes.
110
+
111
+ messages : bool, default True
112
+ Display messages from CHNOSZ?
113
+
114
+ Returns
115
+ -------
116
+ An interactive animated plot.
117
+ """
118
+
119
+ # cap number of frames in animation. Remove limitation after more testing.
120
+ if isinstance(anim_range, list):
121
+ if len(anim_range) == 3:
122
+ if anim_range[2] > 30:
123
+ raise Exception("anim_range is limited to 30 frames.")
124
+ else:
125
+ raise Exception("anim_range must be a list with three values: starting "
126
+ "value of anim_var, stopping value, and number of "
127
+ "frames in the animation")
128
+ else:
129
+ raise Exception("anim_range must be a list with three values: starting "
130
+ "value of anim_var, stopping value, and number of "
131
+ "frames in the animation")
132
+
133
+ if isinstance(basis_args, dict):
134
+ if "species" not in basis_args.keys():
135
+ raise Exception("basis_args needs to contain a list of species for 'species'. "
136
+ "Example: basis_args={'species':['CO2', 'O2', 'H2O', 'H+']}")
137
+ else:
138
+ raise Exception("basis_args needs to be a Python dictionary with a key "
139
+ "called 'species' (additional keys are optional). "
140
+ "Example: basis_args={'species':['CO2', 'O2', 'H2O', 'H+']}")
141
+
142
+
143
+ # Add messages parameter to basis_args if not already present
144
+ if "messages" not in basis_args.keys():
145
+ basis_args["messages"] = messages
146
+
147
+ basis_out = basis(**basis_args)
148
+ basis_sp = list(basis_out.index)
149
+ basis_state = list(basis_out["state"])
150
+
151
+ if isinstance(species_args, dict):
152
+ if "species" not in species_args.keys():
153
+ raise Exception("species_args needs to contain a list of species for 'species'. "
154
+ "Example: species_args={'species':['CO2', 'HCO3-', 'CO3-2']}")
155
+ species_args_list = [species_args]
156
+ elif isinstance(species_args, list):
157
+ species_args_list = species_args
158
+ for species_args in species_args_list:
159
+ if "species" not in species_args.keys():
160
+ raise Exception("species_args needs to contain a list of species for 'species'. "
161
+ "Example: species_args={'species':['CO2', 'HCO3-', 'CO3-2']}")
162
+ else:
163
+ raise Exception("species_args needs to be either a Python dictionary with a key "
164
+ "called 'species' (additional keys are optional). "
165
+ "Example: species_args={'species':['CO2', 'HCO3-', 'CO3-2']}"
166
+ "or else species_args needs to be a list of Python dictionaries."
167
+ "Example: species_args=[{'species':['CO2', 'HCO3-', 'CO3-2'], 'state':[-4]},"
168
+ "{'species':['graphite'], state:[0], 'add':True}]")
169
+
170
+ # There may be multiple arguments passed to species, especially in cases
171
+ # where add=True. Loop through all the arguments to apply them.
172
+ for species_args in species_args_list:
173
+ if "logact" in species_args.keys():
174
+ mod_species_logact = copy.copy(species_args['logact'])
175
+ del species_args['logact']
176
+ else:
177
+ mod_species_logact = []
178
+
179
+ # Add messages parameter to species_args if not already present
180
+ if "messages" not in species_args.keys():
181
+ species_args["messages"] = messages
182
+
183
+ species_out = species(**species_args)
184
+
185
+ if len(mod_species_logact)>0:
186
+ for i in range(0, len(mod_species_logact)) :
187
+ species_out = species(species_args["species"][i], mod_species_logact[i], messages=messages)
188
+
189
+ sp = list(species_out["name"])
190
+
191
+ if isinstance(sp[0], (int, np.integer)):
192
+ sp = [info(s, messages=messages)["name"].values[0] for s in sp]
193
+
194
+ dfs = []
195
+ dmaps = []
196
+ dmaps_names = []
197
+
198
+ if len(anim_range) == 2:
199
+ anim_res = 8
200
+ anim_range = anim_range + [anim_res]
201
+ elif len(anim_range) == 3:
202
+ anim_res = anim_range[2]
203
+ anim_range = [anim_range[0], anim_range[1]]
204
+
205
+ zvals = __seq(anim_range[0], anim_range[1], length_out=anim_res)
206
+
207
+ if "messages" not in affinity_args.keys():
208
+ affinity_args["messages"] = messages
209
+ if "messages" not in diagram_args.keys():
210
+ diagram_args["messages"] = messages
211
+ if "plot_it" not in diagram_args.keys():
212
+ diagram_args["plot_it"] = False
213
+ diagram_args["interactive"] = True
214
+ if "format_names" not in diagram_args.keys():
215
+ format_names=True
216
+ format_x_names=True
217
+ format_y_names=True
218
+
219
+ for z in zvals:
220
+
221
+ if anim_var in basis_out.index:
222
+ basis_out = basis(anim_var, z, messages=messages)
223
+ elif anim_var in list(species_out["name"]):
224
+ species_out = species(anim_var, -z, messages=messages)
225
+ elif anim_var == "pH":
226
+ basis_out = basis("H+", -z, messages=messages)
227
+ else:
228
+ affinity_args[anim_var] = z
229
+
230
+ aeout = affinity(**affinity_args)
231
+
232
+ if equilibrate_args != None:
233
+ equilibrate_args["aout"] = aeout
234
+ if "messages" not in equilibrate_args.keys():
235
+ equilibrate_args["messages"] = messages
236
+ aeout = equilibrate(**equilibrate_args)
237
+
238
+ # Get affinity arguments from the result dictionary
239
+ aeout_args = aeout.get("args", {})
240
+ xvar = list(aeout_args.keys())[0]
241
+ xrange = list(aeout_args[xvar])
242
+
243
+ res_default = 256 # default affinity resolution
244
+ if len(xrange) == 3:
245
+ xres = int(xrange[2])
246
+ else:
247
+ xres = res_default
248
+
249
+ diagram_args["eout"] = aeout
250
+
251
+ # Use diagram_interactive since interactive=True is set
252
+ # Remove 'interactive' key as diagram_interactive doesn't need it
253
+ diagram_args_copy = diagram_args.copy()
254
+ diagram_args_copy.pop('interactive', None)
255
+ df, fig = diagram_interactive(**diagram_args_copy)
256
+
257
+ # Check if this is a predominance plot (2D) or affinity/activity plot (1D)
258
+ if 'pred' not in df.columns:
259
+ # affinity/activity plot (1D) - melt to long format for animation
260
+ is_predom_plot = False
261
+ id_vars = [xvar] # Keep the x-variable as identifier
262
+ value_vars = [col for col in df.columns if col != xvar] # All species columns
263
+ df_melted = df.melt(id_vars=id_vars, value_vars=value_vars,
264
+ var_name='variable', value_name='value')
265
+ df_melted[anim_var] = z
266
+ dfs.append(df_melted)
267
+ else:
268
+ # predominance plot (2D) - keep original format with pred and prednames
269
+ is_predom_plot = True
270
+ df[anim_var] = z
271
+ dfs.append(df)
272
+ yvar = list(aeout_args.keys())[1]
273
+ yrange = list(aeout_args[yvar])
274
+ if len(yrange) == 3:
275
+ yres = int(yrange[2])
276
+ else:
277
+ yres = res_default
278
+
279
+ data = np.array(df.pred)
280
+ shape = (xres, yres)
281
+ dmap = data.reshape(shape)
282
+ dmaps.append(dmap)
283
+
284
+ data = np.array(df.prednames)
285
+ shape = (xres, yres)
286
+ dmap_names = data.reshape(shape)
287
+ dmaps_names.append(dmap_names)
288
+
289
+ xvals = __seq(xrange[0], xrange[1], length_out=xres)
290
+
291
+
292
+ unit_dict = {"P":"bar", "T":"°C", "pH":"", "Eh":"volts", "IS":"mol/kg"}
293
+
294
+ if any([anim_var in basis_out.index, anim_var in list(species_out["name"])]) and anim_var not in unit_dict.keys():
295
+ unit_dict[anim_var] = "logact "+anim_var
296
+
297
+ for i,s in enumerate(basis_sp):
298
+ if basis_state[i] in ["aq", "liq", "cr"]:
299
+ if format_names:
300
+ unit_dict[s] = "log <i>a</i><sub>{}</sub>".format(chemlabel(s))
301
+ else:
302
+ unit_dict[s] = "log <i>a</i><sub>{}</sub>".format(s)
303
+ else:
304
+ if format_names:
305
+ unit_dict[s] = "log <i>f</i><sub>{}</sub>".format(chemlabel(s))
306
+ else:
307
+ unit_dict[s] = "log <i>f</i><sub>{}</sub>".format(s)
308
+
309
+ xlab = xvar+", "+unit_dict[xvar]
310
+
311
+ if xvar in basis_sp:
312
+ xlab = unit_dict[xvar]
313
+ if xvar == "pH":
314
+ xlab = "pH"
315
+
316
+ if is_predom_plot:
317
+ ylab = yvar+", "+unit_dict[yvar]
318
+ if yvar in basis_sp:
319
+ ylab = unit_dict[yvar]
320
+ if yvar == "pH":
321
+ yvar = "pH"
322
+
323
+
324
+ if not is_predom_plot:
325
+
326
+ if 'loga.equil' not in aeout.keys():
327
+ yvar = "A/(2.303RT)"
328
+ else:
329
+ yvar = "log a"
330
+ if "alpha" in diagram_args.keys():
331
+ if diagram_args["alpha"]:
332
+ yvar = "alpha"
333
+
334
+ df_c = pd.concat(dfs)
335
+
336
+ if "fill" in diagram_args.keys():
337
+ if isinstance(diagram_args["fill"], list):
338
+ colormap = {key:col for key,col in zip(list(dict.fromkeys(df_c["variable"])), diagram_args["fill"])}
339
+ else:
340
+ colormap = diagram_args["fill"]
341
+
342
+ # with color mapping
343
+ fig = px.line(df_c, x=xvar, y="value", color='variable', template="simple_white",
344
+ width=500, height=400, animation_frame=anim_var,
345
+ color_discrete_map = colormap,
346
+ labels=dict(value=yvar, x=xvar),
347
+ )
348
+ else:
349
+ # without color mapping
350
+ fig = px.line(df_c, x=xvar, y="value", color='variable', template="simple_white",
351
+ width=500, height=400, animation_frame=anim_var,
352
+ labels=dict(value=yvar, x=xvar),
353
+ )
354
+
355
+ if "annotation" in diagram_args.keys():
356
+ if "annotation_coords" not in diagram_args.keys():
357
+ diagram_args["annotation_coords"] = [0, 0]
358
+ fig.add_annotation(x=diagram_args["annotation_coords"][0],
359
+ y=diagram_args["annotation_coords"][1],
360
+ xref="paper",
361
+ yref="paper",
362
+ align='left',
363
+ text=diagram_args["annotation"],
364
+ bgcolor="rgba(255, 255, 255, 0.5)",
365
+ showarrow=False)
366
+
367
+ if 'main' in diagram_args.keys():
368
+ fig.update_layout(title={'text':diagram_args["main"], 'x':0.5, 'xanchor':'center'})
369
+
370
+ if isinstance(xlab, str):
371
+ fig.update_layout(xaxis_title=xlab)
372
+ if isinstance(ylab, str):
373
+ fig.update_layout(yaxis_title=ylab)
374
+
375
+ if 'fill' in diagram_args.keys():
376
+ if isinstance(diagram_args["fill"], list):
377
+ for i,v in enumerate(diagram_args["fill"]):
378
+ fig['data'][i]['line']['color']=v
379
+
380
+ fig.update_layout(legend_title=None)
381
+
382
+ config = {'displaylogo': False,
383
+ 'modeBarButtonsToRemove': ['resetScale2d', 'toggleSpikelines'],
384
+ 'toImageButtonOptions': {
385
+ 'format': save_format, # one of png, svg, jpeg, webp
386
+ 'filename': save_as,
387
+ 'height': height,
388
+ 'width': width,
389
+ 'scale': save_scale,
390
+ },
391
+ }
392
+
393
+ fig.show(config=config)
394
+ return
395
+
396
+ else:
397
+ yvals = __seq(yrange[0], yrange[1], length_out=yres)
398
+
399
+
400
+
401
+ frames = []
402
+ slider_steps = []
403
+ annotations = []
404
+ cst_data = []
405
+ heatmaps = []
406
+
407
+ # i is a frame in the animation
408
+ for i in range(0, len(zvals)):
409
+
410
+ annotations_i = []
411
+ for s in sp:
412
+ if s in set(dfs[i]["prednames"]):
413
+ # if an annotation should appear, create one for this frame
414
+ df_s = dfs[i].loc[dfs[i]["prednames"]==s,]
415
+ namex = df_s[xvar].mean()
416
+ namey = df_s[yvar].mean()
417
+ a = go.layout.Annotation(
418
+ x=namex,
419
+ y=namey,
420
+ xref="x",
421
+ yref="y",
422
+ text=chemlabel(s),
423
+ bgcolor="rgba(255, 255, 255, 0.5)",
424
+ showarrow=False,
425
+ )
426
+ else:
427
+ # if an annotation shouldn't appear, make an invisible annotation
428
+ # (workaround for a plotly bug where annotations won't clear in an animation)
429
+ namex = statistics.mean(xvals)
430
+ namey = statistics.mean(yvals)
431
+ a = go.layout.Annotation(
432
+ x=namex,
433
+ y=namey,
434
+ xref="x",
435
+ yref="y",
436
+ text="",
437
+ bgcolor="rgba(255, 255, 255, 0)",
438
+ showarrow=False,
439
+ )
440
+ annotations_i.append(a)
441
+
442
+ # allows adding a custom annotation; append to frame
443
+ if "annotation" in diagram_args.keys():
444
+ if "annotation_coords" not in diagram_args.keys():
445
+ diagram_args["annotation_coords"] = [0, 0]
446
+ custom_annotation = go.layout.Annotation(
447
+ x=diagram_args["annotation_coords"][0],
448
+ y=diagram_args["annotation_coords"][1],
449
+ xref="paper",
450
+ yref="paper",
451
+ align='left',
452
+ text=diagram_args["annotation"],
453
+ bgcolor="rgba(255, 255, 255, 0.5)",
454
+ showarrow=False,
455
+ )
456
+ annotations_i.append(custom_annotation)
457
+
458
+ annotations.append(annotations_i)
459
+
460
+ if 'ylab' in diagram_args.keys():
461
+ ylab = diagram_args["ylab"]
462
+ hover_ylab = ylab+': %{y} '
463
+ else:
464
+ ylab = chemlabel(ylab)
465
+ hover_ylab = yvar+': %{y} '+unit_dict[yvar]
466
+
467
+ if 'xlab' in diagram_args.keys():
468
+ xlab = diagram_args["xlab"]
469
+ hover_xlab = xlab+': %{x} '
470
+ else:
471
+ xlab = chemlabel(xlab)
472
+ hover_xlab = xvar+': %{x} '+unit_dict[xvar]
473
+
474
+ heatmaps_i = go.Heatmap(z=dmaps[i], x=xvals, y=yvals, zmin=0, zmax=len(sp)-1,
475
+ customdata=dmaps_names[i],
476
+ hovertemplate=hover_xlab+'<br>'+hover_ylab+'<br>Region: %{customdata}<extra></extra>')
477
+
478
+ heatmaps.append(heatmaps_i)
479
+
480
+ frame = go.Frame(data=[heatmaps_i],
481
+ name=str(i),
482
+ layout=go.Layout(annotations=annotations_i))
483
+
484
+ frames.append(frame)
485
+
486
+ slider_step = dict(
487
+ method='animate',
488
+ label=zvals[i],
489
+ value=i,
490
+ args=[
491
+ [i],
492
+ dict(
493
+ frame=dict(duration=300, redraw=True),
494
+ mode='immediate',
495
+ transition=dict(duration=0)
496
+ )
497
+ ]
498
+ )
499
+
500
+ slider_steps.append(slider_step)
501
+
502
+ fig = go.Figure(
503
+ data = heatmaps[0],
504
+ layout=go.Layout(
505
+ # title="Frame 0",
506
+ title_x=0.5,
507
+ width=500, height=500,
508
+ annotations=annotations[0],
509
+ sliders=[dict(
510
+ active=0,
511
+ yanchor='top',
512
+ xanchor='left',
513
+ currentvalue=dict(
514
+ font=dict(size=12),
515
+ prefix='{}: '.format(anim_var),
516
+ suffix=' '+unit_dict[anim_var],
517
+ visible=True,
518
+ xanchor='right'
519
+ ),
520
+ transition=dict(duration=0, easing='cubic-in-out'),
521
+ pad=dict(b=10, t=50),
522
+ len=0.9,
523
+ x=0.1,
524
+ y=0,
525
+ steps=slider_steps
526
+ )],
527
+ updatemenus=[dict(
528
+ type="buttons",
529
+ buttons=[dict(label="Play",
530
+ method="animate",
531
+ args=[None, {"fromcurrent":True}]),
532
+ dict(label="Pause",
533
+ method="animate",
534
+ args=[[None],
535
+ {"frame": {"duration": 0, "redraw": True},
536
+ "mode": "immediate",
537
+ "transition": {"duration": 0}}],
538
+ )],
539
+ direction="left",
540
+ pad={"r": 10, "t": 87},
541
+ showactive=False,
542
+ x=0.1,
543
+ xanchor="right",
544
+ y=0,
545
+ yanchor="top",
546
+ )]
547
+ ),
548
+ frames=frames
549
+
550
+ )
551
+
552
+
553
+ if 'fill' in diagram_args.keys():
554
+ if isinstance(diagram_args["fill"], list):
555
+ colorscale_temp = []
556
+ for i,v in enumerate(diagram_args["fill"]):
557
+ colorscale_temp.append([i, v])
558
+ colorscale = colorscale_temp
559
+ elif isinstance(diagram_args["fill"], str):
560
+ colorscale = diagram_args["fill"]
561
+ else:
562
+ colorscale = "viridis"
563
+
564
+ fig.update_traces(dict(showscale=False,
565
+ colorscale=colorscale),
566
+ selector={'type':'heatmap'})
567
+
568
+ fig.update_layout(
569
+ xaxis_title=xlab,
570
+ yaxis_title=ylab,
571
+ xaxis={"range":[list(dfs[0][xvar])[0], list(dfs[0][xvar])[-1]]},
572
+ yaxis={"range":[list(dfs[0][yvar])[0], list(dfs[0][yvar])[-1]]},
573
+ margin={"t": 60, "r":60},
574
+ )
575
+
576
+ if 'main' in diagram_args.keys():
577
+ fig.update_layout(title={'text':diagram_args['main'], 'x':0.5, 'xanchor':'center'})
578
+
579
+ config = {'displaylogo': False,
580
+ 'modeBarButtonsToRemove': ['zoom2d', 'pan2d', 'zoomIn2d', 'zoomOut2d',
581
+ 'autoScale2d', 'toggleSpikelines',
582
+ 'hoverClosestCartesian', 'hoverCompareCartesian'],
583
+ 'toImageButtonOptions': {
584
+ 'format': save_format, # one of png, svg, jpeg, webp
585
+ 'filename': save_as,
586
+ 'height': height,
587
+ 'width': width,
588
+ 'scale': save_scale,
589
+ },
590
+ }
591
+
592
+
593
+ fig.show(config=config)