55import shutil
66import re
77import subprocess
8+ from fractions import Fraction
89from typing import List , Dict , Any
910
1011import dpdata
@@ -47,6 +48,8 @@ def phonopy_writefc_commands(arguments: str) -> List[str]:
4748 phonopy_command = Phonon .phonopy_command (arguments )
4849 if setup_command == phonopy_command :
4950 return [setup_command ]
51+ if "--dim" in arguments :
52+ return [setup_command ]
5053 return [setup_command , phonopy_command ]
5154
5255 @staticmethod
@@ -103,15 +106,17 @@ def phonopy_load_commands(
103106 ),
104107 )
105108 )
106- commands .append (
107- Phonon .phonopy_command (
108- Phonon ._join_phonopy_arguments (
109- extra_args ,
110- '--dim=%s -c %s' % (Phonon ._format_dim (supercell_size ), cell_file ),
111- config_file ,
109+ if not shutil .which ("phonopy-init" ):
110+ commands .append (
111+ Phonon .phonopy_command (
112+ Phonon ._join_phonopy_arguments (
113+ extra_args ,
114+ '--dim=%s -c %s'
115+ % (Phonon ._format_dim (supercell_size ), cell_file ),
116+ config_file ,
117+ )
112118 )
113119 )
114- )
115120 else :
116121 commands .append (
117122 Phonon .phonopy_command (
@@ -137,10 +142,51 @@ def phonopy_writefc_load_commands(
137142
138143 @staticmethod
139144 def primitive_axes_setup_argument (primitive_axes : str | None ) -> str :
145+ primitive_axes = Phonon .primitive_axes_config_value (primitive_axes )
140146 if not primitive_axes :
141147 return ""
142148 return f"--pa { primitive_axes } "
143149
150+ @staticmethod
151+ def primitive_axes_config_value (primitive_axes : Any ) -> str | None :
152+ if primitive_axes is None :
153+ return None
154+ if isinstance (primitive_axes , str ):
155+ value = primitive_axes .strip ()
156+ if not value or value .upper () == "AUTO" :
157+ return None
158+ if value .upper () == "P" :
159+ return "P"
160+ tokens = value .replace ("," , " " ).split ()
161+ elif isinstance (primitive_axes , (list , tuple )):
162+ tokens = []
163+ for item in primitive_axes :
164+ if isinstance (item , (list , tuple )):
165+ tokens .extend (item )
166+ else :
167+ tokens .append (item )
168+ else :
169+ raise ValueError (
170+ "PRIMITIVE_AXES must be 'P', 'AUTO', or a 3x3 numeric matrix"
171+ )
172+ if len (tokens ) != 9 :
173+ raise ValueError (
174+ "PRIMITIVE_AXES must be 'P', 'AUTO', or contain exactly 9 values"
175+ )
176+ try :
177+ return " " .join (str (float (Fraction (str (token )))) for token in tokens )
178+ except (ValueError , ZeroDivisionError ) as exc :
179+ raise ValueError ("PRIMITIVE_AXES contains a non-numeric value" ) from exc
180+
181+ @staticmethod
182+ def primitive_axes_phonolammps_argument (primitive_axes : Any ) -> str :
183+ config_value = Phonon .primitive_axes_config_value (primitive_axes )
184+ if config_value is None :
185+ return ""
186+ if config_value == "P" :
187+ config_value = "1 0 0 0 1 0 0 0 1"
188+ return f"-pa { config_value } "
189+
144190 @staticmethod
145191 def run_first_success (commands : List [str ], required_file : str | None = None ) -> None :
146192 errors = []
@@ -154,6 +200,12 @@ def run_first_success(commands: List[str], required_file: str | None = None) ->
154200 return
155201 errors .append (FileNotFoundError (f"{ required_file } was not created by: { command } " ))
156202 if errors :
203+ if required_file is not None :
204+ attempted = "; " .join (commands )
205+ raise RuntimeError (
206+ f"{ required_file } was not created after trying: { attempted } . "
207+ f"Last error: { errors [- 1 ]} "
208+ ) from errors [- 1 ]
157209 raise errors [- 1 ]
158210
159211 @staticmethod
@@ -253,16 +305,30 @@ def _ensure_deepmd_plugin_loaded(self, input_text: str) -> str:
253305 def _build_phonolammps_run_command (self ) -> str :
254306 dim_x , dim_y , dim_z = self .supercell_size
255307 command_template = self .phonolammps_run_command
308+ primitive_axes = self .primitive_axes_phonolammps_argument (
309+ self .PRIMITIVE_AXES
310+ )
256311 if not command_template :
257- return f"phonolammps in.lammps -c POSCAR --dim { dim_x } { dim_y } { dim_z } "
258- return command_template .format (
312+ return self ._join_phonopy_arguments (
313+ f"phonolammps in.lammps -c POSCAR --dim { dim_x } { dim_y } { dim_z } " ,
314+ primitive_axes ,
315+ )
316+ command = command_template .format (
259317 input_file = "in.lammps" ,
260318 poscar = "POSCAR" ,
261319 dim = f"{ dim_x } { dim_y } { dim_z } " ,
262320 dim_x = dim_x ,
263321 dim_y = dim_y ,
264322 dim_z = dim_z ,
323+ primitive_axes = primitive_axes ,
265324 )
325+ if (
326+ primitive_axes
327+ and "{primitive_axes}" not in command_template
328+ and not re .search (r"(?:^|\s)(?:-pa|--primitive_axis)(?:\s|=)" , command )
329+ ):
330+ command = self ._join_phonopy_arguments (command , primitive_axes )
331+ return command
266332
267333 def make_confs (self , path_to_work , path_to_equi , refine = False ):
268334 path_to_work = os .path .abspath (path_to_work )
@@ -408,8 +474,9 @@ def make_confs(self, path_to_work, path_to_equi, refine=False):
408474 ret += "MESH = %s %s %s\n " % (
409475 self .MESH [0 ], self .MESH [1 ], self .MESH [2 ]
410476 )
411- if self .PRIMITIVE_AXES :
412- ret += "PRIMITIVE_AXES = %s\n " % self .PRIMITIVE_AXES
477+ primitive_axes = self .primitive_axes_config_value (self .PRIMITIVE_AXES )
478+ if primitive_axes :
479+ ret += "PRIMITIVE_AXES = %s\n " % primitive_axes
413480 ret += "BAND = %s\n " % self .BAND
414481 if self .BAND_LABELS :
415482 ret += "BAND_LABELS = %s\n " % self .BAND_LABELS
0 commit comments