tilupy 2.0.0__py3-none-any.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.
tilupy/notations.py ADDED
@@ -0,0 +1,866 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ from __future__ import annotations
5
+ import tilupy
6
+ import pandas as pd
7
+
8
+
9
+ LABEL_OPTIONS = dict(language="english", label_type="symbol")
10
+ """Dictionary of configuration options for label generation.
11
+
12
+ This dictionary specifies the settings used for generating or formatting labels.
13
+ It includes the language of the labels and the type of label representation.
14
+
15
+ Keys:
16
+ - language : str
17
+ Language used for the labels (e.g., "english").
18
+ - label_type : str
19
+ Type of label representation (e.g., "symbol" for symbolic labels).
20
+ """
21
+
22
+ class Notation:
23
+ """Notation system for physical quantities, symbols, or variables.
24
+
25
+ This class allows the definition of a notation with a name, long name, gender, symbol, and unit.
26
+ It provides properties to access and modify these attributes, and a method to retrieve the long name
27
+ in a specific language.
28
+
29
+ Parameters
30
+ ----------
31
+ name : str
32
+ The short name or identifier of the notation.
33
+ long_name : tilupy.notations.LongName or str, optional
34
+ The long name of the notation. If a dictionary is provided, it is converted to a
35
+ :any:`tilupy.notations.LongName` object. By default None.
36
+ gender : tilupy.notations.Gender, optional
37
+ The gender associated with the notation. If a dictionary is provided, it is converted to a
38
+ :obj:`tilupy.notations.Gender` object. By default None.
39
+ symbol : str, optional
40
+ The symbol representing the notation. By default None.
41
+ unit : tilupy.notations.Unit, optional
42
+ The unit associated with the notation. By default None.
43
+
44
+ Attributes
45
+ ----------
46
+ _name : str
47
+ The short name or identifier of the notation.
48
+ _long_name : tilupy.notations.LongName or str
49
+ The long name of the notation, which can be language-specific or gender-specific.
50
+ _gender : tilupy.notations.Gender
51
+ The gender associated with the notation, if applicable.
52
+ _symbol : str
53
+ The symbol representing the notation (e.g., mathematical symbol).
54
+ _unit : tilupy.notations.Unit
55
+ The unit associated with the notation (e.g., physical unit).
56
+ """
57
+ def __init__(self,
58
+ name: str,
59
+ long_name: tilupy.notations.LongName | str = None,
60
+ gender: tilupy.notations.Gender | str = None,
61
+ symbol: str = None,
62
+ unit: tilupy.notations.Unit = None):
63
+ self._name = name
64
+ self._long_name = long_name
65
+ self._gender = gender
66
+ self._symbol = symbol
67
+ self._unit = unit
68
+
69
+
70
+ @property
71
+ def name(self):
72
+ """Get name.
73
+
74
+ Returns
75
+ -------
76
+ str
77
+ Attribute :attr:`_name`
78
+ """
79
+ return self._name
80
+
81
+ @name.setter
82
+ def name(self, value: str):
83
+ """Set name.
84
+
85
+ Parameters
86
+ ----------
87
+ value : str
88
+ Name value
89
+ """
90
+ if value is None:
91
+ self._name = None
92
+ else:
93
+ self._name = value
94
+
95
+ @property
96
+ def unit(self):
97
+ """Get unit.
98
+
99
+ Returns
100
+ -------
101
+ tilupy.notations.Unit
102
+ Attribute :attr:`_unit`
103
+ """
104
+ return self._unit
105
+
106
+ @unit.setter
107
+ def unit(self, value: str):
108
+ """Set unit.
109
+
110
+ Parameters
111
+ ----------
112
+ value : tilupy.notations.Unit
113
+ Unit value
114
+ """
115
+ if value is None:
116
+ self._unit = None
117
+ else:
118
+ self._unit = value
119
+
120
+ @property
121
+ def gender(self):
122
+ """Get gender.
123
+
124
+ Returns
125
+ -------
126
+ tilupy.notations.Gender
127
+ Attribute :attr:`_gender`
128
+ """
129
+ return self._gender
130
+
131
+ @gender.setter
132
+ def gender(self, value):
133
+ """Set unit.
134
+
135
+ Parameters
136
+ ----------
137
+ value : tilupy.notations.Gender
138
+ Gender value
139
+ """
140
+ if value is None:
141
+ self._gender = Gender()
142
+ elif isinstance(value, dict):
143
+ self._gender = Gender(**value)
144
+ else:
145
+ self._gender = value
146
+
147
+ @property
148
+ def symbol(self):
149
+ """Get symbol.
150
+
151
+ Returns
152
+ -------
153
+ str
154
+ Attribute :attr:`_symbol`
155
+ """
156
+ return self._symbol
157
+
158
+ @symbol.setter
159
+ def symbol(self, value: str):
160
+ """Set symbol.
161
+
162
+ Parameters
163
+ ----------
164
+ value : str
165
+ Symbol value
166
+ """
167
+ if value is None:
168
+ self._symbol = None
169
+ else:
170
+ self._symbol = value
171
+
172
+ @property
173
+ def long_name(self):
174
+ """Get long name.
175
+
176
+ Returns
177
+ -------
178
+ tilupy.notations.LongName
179
+ Attribute :attr:`_long_name`
180
+ """
181
+ return self._long_name
182
+
183
+ @long_name.setter
184
+ def long_name(self, value):
185
+ """Set long name.
186
+
187
+ Parameters
188
+ ----------
189
+ value : tilupy.notations.LongName
190
+ Long name value
191
+ """
192
+ if value is None:
193
+ self._long_name = LongName()
194
+ elif isinstance(value, dict):
195
+ self._long_name = LongName(**value)
196
+ else:
197
+ self._long_name = value
198
+
199
+ def get_long_name(self,
200
+ language: str = None,
201
+ gender: str = None
202
+ ) -> str:
203
+ """Retrieve the long name of the notation in the specified language and gender form.
204
+
205
+ The method retrieves the long name in the specified language (defaulting to the language
206
+ in :data:`tilupy.notations.LABEL_OPTIONS`). If a gender is provided, the method returns
207
+ the gender-specific form of the long name.
208
+
209
+ Parameters
210
+ ----------
211
+ language : str, optional
212
+ The language in which to retrieve the long name. If not provided, the language from
213
+ :data:`tilupy.notations.LABEL_OPTIONS` is used.
214
+ gender : str, optional
215
+ The gender form of the long name to retrieve. If not provided, the default form is returned.
216
+
217
+ Returns
218
+ -------
219
+ str
220
+ The long name in the specified language and gender form. If the long name is a string, it is returned as-is.
221
+ """
222
+ if isinstance(self._long_name, str):
223
+ return self._long_name
224
+
225
+ if language is None:
226
+ language = LABEL_OPTIONS["language"]
227
+
228
+ if isinstance(self._long_name, dict):
229
+ return self._long_name[language]
230
+
231
+ res = getattr(self._long_name, language)
232
+ if gender is not None:
233
+ res = res[gender]
234
+
235
+ return res
236
+
237
+
238
+ class Unit(pd.Series):
239
+ """Subclass of pandas.Series to represent physical units and their exponents.
240
+
241
+ This class allows the creation of unit objects as combinations of base units (e.g., "Pa", "N", "kg")
242
+ with their respective exponents. It supports basic operations like multiplication and provides
243
+ a method to generate a LaTeX-formatted label for the unit combination.
244
+
245
+ Parameters
246
+ ----------
247
+ series : pandas.Series, optional
248
+ An existing Series to initialize the Unit object.
249
+ **kwargs : dict
250
+ Key-value pairs where keys are unit names (from `Unit.UNITS`)
251
+ and values are their exponents (as integers or floats).
252
+ If provided, only units in `Unit.UNITS` are allowed.
253
+
254
+ Raises
255
+ ------
256
+ ValueError
257
+ If a key in `kwargs` is not in `Unit.UNITS`.
258
+ """
259
+
260
+ UNITS = ["Pa", "N", "kg", "m", "s", "J"]
261
+ """List of available units."""
262
+
263
+ def __init__(self,
264
+ series: pd.Series = None,
265
+ **kwargs):
266
+ if series is not None:
267
+ super().__init__(series)
268
+ else:
269
+ super().__init__(dtype="object")
270
+ for key in kwargs:
271
+ if key not in Unit.UNITS:
272
+ raise ValueError("unrecognized unit")
273
+ self[key] = kwargs[key]
274
+
275
+
276
+ def __mul__(self, other):
277
+ """Multiply two Unit objects.
278
+
279
+ The multiplication combines the exponents of matching units.
280
+ Units with zero exponents are dropped from the result.
281
+
282
+ Parameters
283
+ ----------
284
+ other : tilupy.notations.Unit
285
+ Another :class:`tilupy.notations.Unit` object to multiply with.
286
+
287
+ Returns
288
+ -------
289
+ tilupy.notations.Unit
290
+ A new Unit object representing the product of the two units.
291
+ """
292
+ tmp = self.add(other, fill_value=0)
293
+ return Unit(tmp[tmp != 0])
294
+
295
+
296
+ def get_label(self):
297
+ """Generate a LaTeX-formatted string representation of the unit.
298
+
299
+ The label combines positive and negative exponents, omitting exponents of 1.
300
+ Positive exponents are written as superscripts, while negative exponents
301
+ are enclosed in curly braces (for LaTeX compatibility).
302
+
303
+ Returns
304
+ -------
305
+ str
306
+ A LaTeX-formatted string representing the unit (e.g., "kg m s$^{-2}$").
307
+ Returns an empty string if the Unit object is empty.
308
+ """
309
+ if self.empty:
310
+ return ""
311
+
312
+ positives = self[self >= 1].reindex(Unit.UNITS).dropna()
313
+ negatives = self[self < 0].reindex(Unit.UNITS).dropna()
314
+ text_label = [
315
+ index + "$^{:.0f}$".format(positives[index])
316
+ for index in positives.index
317
+ ]
318
+ text_label += [
319
+ index + "$^{{{:.0f}}}$".format(negatives[index])
320
+ for index in negatives.index
321
+ ]
322
+ text_label = " ".join(text_label)
323
+ text_label = text_label.replace("$^1$", "")
324
+
325
+ return text_label
326
+
327
+
328
+ class LongName(object):
329
+ """Generic container class to dynamically store LongName attributes as key-value pairs.
330
+
331
+ This class allows the creation of objects with arbitrary attributes,
332
+ which are passed as keyword arguments during initialization.
333
+
334
+ Parameters
335
+ ----------
336
+ **kwargs : dict
337
+ Arbitrary keyword arguments. Each key-value pair is added as an attribute to
338
+ the object.
339
+ """
340
+ def __init__(self, **kwargs):
341
+ self.__dict__.update(kwargs)
342
+
343
+
344
+ class Gender(object):
345
+ """Generic container class to dynamically store Gender attributes as key-value pairs.
346
+
347
+ This class allows the creation of objects with arbitrary attributes,
348
+ which are passed as keyword arguments during initialization.
349
+
350
+ Parameters
351
+ ----------
352
+ **kwargs : dict
353
+ Arbitrary keyword arguments. Each key-value pair is added as an attribute to
354
+ the object.
355
+ """
356
+ def __init__(self, **kwargs):
357
+ self.__dict__.update(kwargs)
358
+
359
+
360
+ NOTATIONS = dict()
361
+ """Dictionary containing predefined notations.
362
+
363
+ Pre-made notations:
364
+
365
+ - x [m]
366
+ - y [m]
367
+ - d [m]
368
+ - z [m]
369
+ - t [s]
370
+ - xy [m2]
371
+ - h [m]
372
+ - hvert [m]
373
+ - u [m1.s-1]
374
+ - hu [m2.s-1]
375
+ - ek [J]
376
+
377
+ Also some operators:
378
+
379
+ - max
380
+ - int
381
+ """
382
+
383
+ NOTATIONS["x"] = Notation("x",
384
+ symbol="X",
385
+ unit=Unit(m=1),
386
+ long_name=LongName(english="X",
387
+ french="X"),
388
+ gender=Gender(english=None,
389
+ french="f"),
390
+ )
391
+ NOTATIONS["y"] = Notation("y",
392
+ symbol="Y",
393
+ unit=Unit(m=1),
394
+ long_name=LongName(english="Y",
395
+ french="Y"),
396
+ gender=Gender(english=None,
397
+ french="f"),
398
+ )
399
+ NOTATIONS["d"] = Notation("d",
400
+ symbol="D",
401
+ unit=Unit(m=1),
402
+ long_name=LongName(english="Distance",
403
+ french="Distance"),
404
+ gender=Gender(english=None,
405
+ french="f"),
406
+ )
407
+ NOTATIONS["z"] = Notation("z",
408
+ symbol="z",
409
+ unit=Unit(m=1),
410
+ long_name=LongName(english="Altitude",
411
+ french="Altitude"),
412
+ gender=Gender(english=None,
413
+ french="f"),
414
+ )
415
+ NOTATIONS["t"] = Notation("t",
416
+ symbol="t",
417
+ unit=Unit(s=1),
418
+ long_name=LongName(english="Time",
419
+ french="Temps"),
420
+ gender=Gender(english=None,
421
+ french="m"),
422
+ )
423
+ NOTATIONS["xy"] = Notation("xy",
424
+ symbol="XY",
425
+ unit=Unit(m=2),
426
+ long_name=LongName(english="Surface",
427
+ french="Surface"),
428
+ gender=Gender(english=None,
429
+ french="f"),
430
+ )
431
+ NOTATIONS["h"] = Notation("h",
432
+ symbol="h",
433
+ unit=Unit(m=1),
434
+ long_name=LongName(english="Thickness",
435
+ french="Epaisseur"),
436
+ gender=Gender(english=None,
437
+ french="f"),
438
+ )
439
+ NOTATIONS["hvert"] = Notation("hvert",
440
+ symbol="h^v",
441
+ unit=Unit(m=1),
442
+ long_name=LongName(english="Vertical thickness",
443
+ french="Epaisseur verticale"),
444
+ gender=Gender(english=None,
445
+ french="f"),
446
+ )
447
+ NOTATIONS["u"] = Notation("u",
448
+ symbol="u",
449
+ unit=Unit(m=1, s=-1),
450
+ long_name=LongName(english="Velocity",
451
+ french="Vitesse"),
452
+ gender=Gender(english=None,
453
+ french="f"),
454
+ )
455
+ NOTATIONS["hu"] = Notation("hu",
456
+ symbol="hu",
457
+ unit=Unit(m=2, s=-1),
458
+ long_name=LongName(english="Momentum",
459
+ french="Moment"),
460
+ gender=Gender(english=None,
461
+ french="m"),
462
+ )
463
+ NOTATIONS["hu2"] = Notation("hu2",
464
+ symbol="hu2",
465
+ unit=Unit(m=3, s=-2),
466
+ long_name=None,
467
+ gender=None,
468
+ )
469
+ NOTATIONS["ek"] = Notation("ek",
470
+ symbol="ek",
471
+ unit=Unit(J=1),
472
+ long_name=LongName(english="Kinetic energy",
473
+ french="Energie cinétique"),
474
+ gender=Gender(english=None,
475
+ french="f"),
476
+ )
477
+
478
+ NOTATIONS["max"] = Notation("max",
479
+ symbol="max",
480
+ unit=None,
481
+ long_name=LongName(english="Maximum",
482
+ french=dict(m="Maximum", f="Maximale")),
483
+ gender=None,
484
+ )
485
+ NOTATIONS["int"] = Notation("int",
486
+ symbol="int",
487
+ unit=None,
488
+ long_name=LongName(english="Integrate",
489
+ french=dict(m="Intégré", f="Intégrée")),
490
+ gender=None,
491
+ )
492
+
493
+
494
+ def get_notation(name: str,
495
+ language: str = None
496
+ ) -> tilupy.notations.Notation:
497
+ """Retrieve or construct a Notation object for a given name.
498
+
499
+ This function attempts to fetch a predefined notation from the global :data:`tilupy.notations.NOTATIONS`
500
+ dictionary. If the notation is not found, it constructs a new :class:`tilupy.notations.Notation` object
501
+ based on the provided name. For composite names (e.g., "state_operator"), it recursively resolves the
502
+ state and operator, then combines them using :func:`tilupy.notations.add_operator`.
503
+
504
+ Parameters
505
+ ----------
506
+ name : str
507
+ The name of the notation to retrieve or construct.
508
+ Can be a simple name (e.g., "velocity") or a composite name (e.g., "velocity_int_t").
509
+ language : str, optional
510
+ The language to use for the long name of the notation. If not provided, the default language from `LABEL_OPTIONS` is used.
511
+
512
+ Returns
513
+ -------
514
+ tilupy.notations.Notation
515
+ The retrieved or constructed :class:`tilupy.notations.Notation` object.
516
+ """
517
+ try:
518
+ notation = NOTATIONS[name]
519
+ except KeyError:
520
+ strings = name.split("_")
521
+ if len(strings) == 1:
522
+ notation = Notation(name,
523
+ symbol=name,
524
+ unit=None,
525
+ long_name=name,
526
+ gender=None)
527
+ else:
528
+ state = get_notation(strings[0])
529
+ operator = get_notation(strings[1])
530
+ if len(strings) == 3:
531
+ axis = strings[2]
532
+ else:
533
+ axis = None
534
+ notation = add_operator(state,
535
+ operator,
536
+ axis=axis,
537
+ language=language)
538
+
539
+ return notation
540
+
541
+
542
+ def get_operator_unit(name: str,
543
+ axis: str
544
+ ) -> tilupy.notations.Unit:
545
+ """Determine the unit associated with an operator based on its name and axis.
546
+
547
+ This function returns a :class:`tilupy.notations.Unit` object corresponding to the operator's
548
+ name and the axis it operates on. For example, an integral operator ("int") has different units
549
+ depending on whether it integrates over time ("t") or space ("x", "y", "xy").
550
+
551
+ Parameters
552
+ ----------
553
+ name : str
554
+ The name of the operator (e.g., "int").
555
+ axis : str or None
556
+ The axis over which the operator acts (e.g., "t", "x", "y", "xy").
557
+ If None, the default axis is used (e.g., "t" for "int").
558
+
559
+ Returns
560
+ -------
561
+ tilupy.notations.Unit
562
+ The unit associated with the operator and axis.
563
+ """
564
+ if name == "int":
565
+ if axis == "t" or axis is None:
566
+ unit = Unit(s=1)
567
+ if axis in ["x", "y"]:
568
+ unit = Unit(m=1)
569
+ if axis == "xy":
570
+ unit = Unit(m=2)
571
+ else:
572
+ unit = Unit(pd.Series(dtype="object"))
573
+ return unit
574
+
575
+
576
+ def make_long_name(notation: tilupy.notations.Notation,
577
+ operator: tilupy.notations.Notation,
578
+ language: str = None
579
+ ) -> str:
580
+ """Construct a long name for a notation combined with an operator.
581
+
582
+ This function generates a readable long name by combining the long names of a notation and an operator.
583
+ It can be written in French or English.
584
+
585
+ Parameters
586
+ ----------
587
+ notation : tilupy.notations.Notation
588
+ The base notation object.
589
+ operator : tilupy.notations.Notation
590
+ The operator notation object.
591
+ language : str, optional
592
+ The language to use for the long name. If not provided, the default language from
593
+ :data:`tilupy.notations.LABEL_OPTIONS` is used. By default None.
594
+
595
+ Returns
596
+ -------
597
+ str
598
+ The combined long name in the specified language.
599
+ """
600
+ if language is None:
601
+ language = LABEL_OPTIONS["language"]
602
+
603
+ str_notation = notation.get_long_name(language=language)
604
+ try:
605
+ gender = gender = getattr(notation.gender, language)
606
+ except AttributeError:
607
+ gender = None
608
+ str_operator = operator.get_long_name(language=language, gender=gender)
609
+
610
+ if language == "english":
611
+ res = str_operator + " " + str_notation
612
+ elif language == "french":
613
+ res = str_notation + " " + str_operator
614
+
615
+ return res
616
+
617
+
618
+ def add_operator(notation: tilupy.notations.Notation | str,
619
+ operator: tilupy.notations.Notation | str,
620
+ axis: str = None,
621
+ language: str = None
622
+ ) -> tilupy.notations.Notation:
623
+ """Combine a notation with an operator to create a new notation.
624
+
625
+ This function constructs a new :class:`tilupy.notations.Notation` object by combining
626
+ a base notation with an operator. It handles the symbol, unit, and long name of the
627
+ resulting notation, taking into account the operator's axis (if any).
628
+
629
+ Parameters
630
+ ----------
631
+ notation : tilupy.notations.Notation or str
632
+ The base notation object or its name.
633
+ operator : tilupy.notations.Notation or str
634
+ The operator notation object or its name.
635
+ axis : str, optional
636
+ The axis over which the operator acts (e.g., "t", "x", "y"). By default None.
637
+ language : str, optional
638
+ The language to use for the long name. If not provided, the default language from
639
+ :data:`tilupy.notations.LABEL_OPTIONS` is used. By default None.
640
+
641
+ Returns
642
+ -------
643
+ tilupy.notations.Notation
644
+ The new Notation object resulting from the combination of the base notation and the operator.
645
+ """
646
+ if isinstance(operator, str):
647
+ operator = get_notation(operator)
648
+
649
+ if isinstance(notation, str):
650
+ notation = get_notation(notation)
651
+
652
+ operator_symbol = operator.symbol
653
+ if axis is not None:
654
+ operator_symbol += "({})".format(axis)
655
+
656
+ unit_operator = get_operator_unit(operator.name, axis)
657
+
658
+ if operator.name == "int":
659
+ if axis is None:
660
+ ll = "t" # If axis is not specified by default integration is over time
661
+ else:
662
+ ll = axis
663
+ symbol = "\\int_{{{}}} {}".format(ll, notation.symbol)
664
+ else:
665
+ operator_symbol = operator.symbol
666
+ if axis is not None:
667
+ operator_symbol += "({})".format(axis)
668
+ symbol = notation.symbol + "_{{{}}}".format(operator_symbol)
669
+
670
+ if notation.unit is None:
671
+ unit = None
672
+ else:
673
+ unit = notation.unit * unit_operator
674
+
675
+ res = Notation(name=notation.name + "_" + operator.name,
676
+ symbol=symbol,
677
+ unit=unit,
678
+ long_name=make_long_name(notation, operator, language=language),
679
+ )
680
+ return res
681
+
682
+
683
+ def get_label(notation: tilupy.notations.Notation,
684
+ with_unit: bool = True,
685
+ label_type: str = None,
686
+ language: str = None
687
+ ) -> str:
688
+ """Generate a formatted label for a notation.
689
+
690
+ This function creates a label for a notation, either in literal form (long name)
691
+ or symbolic form (symbol). It can optionally include the unit in the label.
692
+
693
+ Parameters
694
+ ----------
695
+ notation : tilupy.notations.Notation or str
696
+ The notation object or its name.
697
+ with_unit : bool, optional
698
+ If True, the unit is included in the label. By default True.
699
+ label_type : str, optional
700
+ The type of label to generate: "litteral" (long name) or "symbol" (symbol).
701
+ If not provided, the default label type from :data:`tilupy.notations.LABEL_OPTIONS`
702
+ is used. By default None.
703
+ language : str, optional
704
+ The language to use for the label. If not provided, the default language from
705
+ :data:`tilupy.notations.LABEL_OPTIONS` is used.
706
+ By default None.
707
+
708
+ Returns
709
+ -------
710
+ str
711
+ The formatted label for the notation, optionally including the unit.
712
+ """
713
+ if isinstance(notation, str):
714
+ notation = get_notation(notation)
715
+
716
+ if label_type is None:
717
+ label_type = LABEL_OPTIONS["label_type"]
718
+ if language is None:
719
+ language = LABEL_OPTIONS["language"]
720
+
721
+ if label_type == "litteral":
722
+ label = notation.get_long_name(language=language, gender=None)
723
+ elif label_type == "symbol":
724
+ label = "$" + notation.symbol + "$"
725
+
726
+ if with_unit and notation.unit is not None:
727
+ unit_string = notation.unit.get_label()
728
+ # Add unit only if string is not empty
729
+ if unit_string:
730
+ label = label + " ({})".format(unit_string)
731
+
732
+ return label
733
+
734
+
735
+ def set_label_options(**kwargs):
736
+ """Update the global label options.
737
+
738
+ This function updates the global :data:`tilupy.notations.LABEL_OPTIONS` dictionary
739
+ with the provided keyword arguments. It allows dynamic configuration of label
740
+ generation settings, such as language and label type.
741
+
742
+ Parameters
743
+ ----------
744
+ **kwargs : dict
745
+ Key-value pairs to update in :data:`tilupy.notations.LABEL_OPTIONS`.
746
+ Valid keys include "language" and "label_type".
747
+ """
748
+ global LABEL_OPTIONS
749
+ LABEL_OPTIONS.update(**kwargs)
750
+
751
+
752
+ def readme_to_params(file: str,
753
+ readme_param_match: dict = None
754
+ ) -> dict:
755
+ """Convert a README file into a dictionary of parameters.
756
+
757
+ This function reads a README file and extracts key-value pairs, optionally using a mapping dictionary
758
+ to rename the keys. Each line in the file is expected to contain a key and a value separated by whitespace.
759
+
760
+ Parameters
761
+ ----------
762
+ file : str
763
+ Path to the README file to read.
764
+ readme_param_match : dict, optional
765
+ A dictionary mapping keys in the README file to new keys in the output dictionary.
766
+ If not provided, the keys are used as-is. By default None.
767
+
768
+ Returns
769
+ -------
770
+ dict
771
+ A dictionary of parameters extracted from the README file.
772
+ """
773
+ params = dict()
774
+ with open(file, "r") as f:
775
+ if readme_param_match is not None:
776
+ for line in f:
777
+ (key, val) = line.split()
778
+ if key in readme_param_match:
779
+ params[readme_param_match[key]] = val
780
+ else:
781
+ for line in f:
782
+ (key, val) = line.split()
783
+ params[key] = val
784
+
785
+ return params
786
+
787
+
788
+ def make_rheol_string_fmt(rheoldict: dict) -> str:
789
+ """Generate a formatted string template for rheological parameters.
790
+
791
+ This function constructs a string template for rheological parameters based on the provided dictionary and rheological law.
792
+ The template includes placeholders for parameter values, which can later be formatted with specific values.
793
+
794
+ Parameters
795
+ ----------
796
+ rheoldict : dict
797
+ A dictionary of rheological parameters (e.g., "delta1", "ksi").
798
+
799
+ Returns
800
+ -------
801
+ str
802
+ A formatted string template for the rheological parameters.
803
+ """
804
+ text = ""
805
+ for name in ["delta1", "delta2", "delta3", "delta4"]:
806
+ if name in rheoldict:
807
+ new_txt = name + "_{:05.2f}_"
808
+ text += new_txt
809
+ if "ksi" in rheoldict:
810
+ new_txt = name + "ksi_{:06.1f}_"
811
+ text += new_txt
812
+ text = text[:-1]
813
+
814
+ return text
815
+
816
+
817
+ def make_rheol_string(rheoldict: dict,
818
+ law: str
819
+ ) -> str | list[str]:
820
+ """Generate formatted strings for rheological parameters.
821
+
822
+ This function creates formatted strings for rheological parameters based on the provided dictionary and rheological law.
823
+ It handles multiple parameter sets (e.g., for different time steps or conditions) and formats each set according to the specified law.
824
+
825
+ Parameters
826
+ ----------
827
+ rheoldict : dict
828
+ A dictionary of rheological parameters. Values can be lists of parameters for multiple sets.
829
+ law : str
830
+ The rheological law to use. Can be "coulomb", "voellmy" or "pouliquen_2002".
831
+
832
+ Returns
833
+ -------
834
+ str or list[str]
835
+ The formatted string(s) for the rheological parameters.
836
+ If there is only one parameter set, a single string is returned. Otherwise, a list of strings is returned.
837
+ """
838
+ keys = [key for key in rheoldict]
839
+ for key in keys:
840
+ if not isinstance(rheoldict[key], list):
841
+ rheoldict[key] = [rheoldict[key]]
842
+
843
+ nparams = len(rheoldict[keys[0]])
844
+ txt_fmt = make_rheol_string_fmt(rheoldict)
845
+ texts = []
846
+
847
+ for i in range(nparams):
848
+ if law == "coulomb":
849
+ txt_fmt = "delta1_{:05.2f}"
850
+ text = txt_fmt.format(rheoldict["delta1"][i]).replace(".", "p")
851
+ if law == "voellmy":
852
+ txt_fmt = "delta1_{:05.2f}_ksi_{:06.1f}"
853
+ text = txt_fmt.format(
854
+ rheoldict["delta1"][i], rheoldict["ksi"][i]
855
+ ).replace(".", "p")
856
+ if law == "pouliquen_2002":
857
+ txt_fmt = "delta1_{:05.2f}_L_{:05.2f}"
858
+ text = txt_fmt.format(
859
+ rheoldict["delta1"][i], rheoldict["wlong"][i]
860
+ ).replace(".", "p")
861
+ texts.append(text)
862
+
863
+ if nparams == 1:
864
+ texts = texts[0]
865
+
866
+ return texts